@xrift/world-components 0.40.0 → 0.40.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { type Object3D } from 'three';
|
|
2
2
|
/**
|
|
3
3
|
* Y軸ビルボードフック
|
|
4
|
-
* 対象の Object3D を毎フレームカメラに向けてY
|
|
5
|
-
*
|
|
4
|
+
* 対象の Object3D を毎フレームカメラに向けて Y 軸のみ回転させる。
|
|
5
|
+
* 親のワールド回転を考慮し、ローカル回転として正しい値を設定する。
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
* スタック構造で多重ネスト(Mirror + WebXR左右眼)にも対応。
|
|
7
|
+
* 実装方針:
|
|
8
|
+
* - useFrame で renderer.render() の前に rotation を確定する
|
|
9
|
+
* - 1 フレーム中に複数の renderer.render 呼び出しがあっても全て同じ matrixWorld
|
|
10
|
+
* を使うので、複数 BillboardY 同居や opaque/transparent 混在でも安定して動作する
|
|
12
11
|
*
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* カメラ真上/真下対応:
|
|
13
|
+
* - target がカメラの真上/真下にあると atan2(0, 0) が暴れるため、水平距離が
|
|
14
|
+
* {@link NEAR_CAMERA_DIST_SQ} 以下のときは「カメラの視線方向 1m 先」を
|
|
15
|
+
* 仮想カメラ位置として使い、そこから target を見ているとして rotation を計算する
|
|
16
|
+
* (視線方向に合わせて billboard が回転する形になる)
|
|
17
|
+
* - 視線が真上/真下のときは forward の XZ 成分が無くなるので前フレームの rotation
|
|
18
|
+
* を保持する
|
|
19
|
+
*
|
|
20
|
+
* 既知の制約:
|
|
21
|
+
* - Mirror(Reflector)の鏡像内では billboard が「鏡カメラに向く」挙動はしない
|
|
22
|
+
* (メインカメラ向きで固定される)。鏡像内でも正しい向きにしたいケースは
|
|
23
|
+
* 別途検討が必要(issue #173 参照)。
|
|
17
24
|
*/
|
|
18
25
|
export declare const useBillboardY: <T extends Object3D>() => import("react").RefObject<T>;
|
|
19
26
|
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/components/BillboardY/hooks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/components/BillboardY/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,OAAO,CAAA;AAuBd;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,QAAQ,mCAiD/C,CAAA"}
|
|
@@ -1,108 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import { useFrame } from '@react-three/fiber';
|
|
3
|
+
import { Euler, Quaternion, Vector3, } from 'three';
|
|
3
4
|
import { getBillboardYRotation } from './utils';
|
|
4
5
|
const _cameraWorldPos = new Vector3();
|
|
5
6
|
const _targetWorldPos = new Vector3();
|
|
7
|
+
const _cameraForward = new Vector3();
|
|
8
|
+
const _virtualCameraPos = new Vector3();
|
|
6
9
|
const _parentQuat = new Quaternion();
|
|
7
10
|
const _parentPos = new Vector3();
|
|
8
11
|
const _parentScale = new Vector3();
|
|
9
12
|
const _euler = new Euler();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const TRANSPARENT_MATERIAL = new MeshBasicMaterial({
|
|
20
|
-
colorWrite: false,
|
|
21
|
-
depthWrite: false,
|
|
22
|
-
depthTest: false,
|
|
23
|
-
transparent: true,
|
|
24
|
-
opacity: 0,
|
|
25
|
-
});
|
|
13
|
+
/** カメラとターゲットが水平方向にこれ以下に近接していたら「仮想カメラ位置」へ
|
|
14
|
+
* フォールバックする閾値。0.1m = 10cm。
|
|
15
|
+
*
|
|
16
|
+
* 自分の頭上に置く NameTag / TagDisplay のように XZ 座標がカメラと一致するケースで
|
|
17
|
+
* atan2(0, 0) が暴れるのを防ぐ。歩行時の frame ラグ(〜25mm/frame)や
|
|
18
|
+
* 走行時のラグ(〜83mm/frame)もこの範囲に収まる。 */
|
|
19
|
+
const NEAR_CAMERA_DIST_SQ = 0.01; // (0.1m)²
|
|
20
|
+
/** 仮想カメラを実カメラの視線方向何メートル先に置くか */
|
|
21
|
+
const VIRTUAL_CAMERA_DISTANCE = 1.0;
|
|
26
22
|
/**
|
|
27
23
|
* Y軸ビルボードフック
|
|
28
|
-
* 対象の Object3D を毎フレームカメラに向けてY
|
|
29
|
-
*
|
|
24
|
+
* 対象の Object3D を毎フレームカメラに向けて Y 軸のみ回転させる。
|
|
25
|
+
* 親のワールド回転を考慮し、ローカル回転として正しい値を設定する。
|
|
26
|
+
*
|
|
27
|
+
* 実装方針:
|
|
28
|
+
* - useFrame で renderer.render() の前に rotation を確定する
|
|
29
|
+
* - 1 フレーム中に複数の renderer.render 呼び出しがあっても全て同じ matrixWorld
|
|
30
|
+
* を使うので、複数 BillboardY 同居や opaque/transparent 混在でも安定して動作する
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* カメラ真上/真下対応:
|
|
33
|
+
* - target がカメラの真上/真下にあると atan2(0, 0) が暴れるため、水平距離が
|
|
34
|
+
* {@link NEAR_CAMERA_DIST_SQ} 以下のときは「カメラの視線方向 1m 先」を
|
|
35
|
+
* 仮想カメラ位置として使い、そこから target を見ているとして rotation を計算する
|
|
36
|
+
* (視線方向に合わせて billboard が回転する形になる)
|
|
37
|
+
* - 視線が真上/真下のときは forward の XZ 成分が無くなるので前フレームの rotation
|
|
38
|
+
* を保持する
|
|
36
39
|
*
|
|
37
|
-
*
|
|
38
|
-
* -
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* 既知の制約:
|
|
41
|
+
* - Mirror(Reflector)の鏡像内では billboard が「鏡カメラに向く」挙動はしない
|
|
42
|
+
* (メインカメラ向きで固定される)。鏡像内でも正しい向きにしたいケースは
|
|
43
|
+
* 別途検討が必要(issue #173 参照)。
|
|
41
44
|
*/
|
|
42
45
|
export const useBillboardY = () => {
|
|
43
46
|
const ref = useRef(null);
|
|
44
|
-
|
|
45
|
-
if (!ref.current)
|
|
46
|
-
return;
|
|
47
|
+
useFrame(({ camera }) => {
|
|
47
48
|
const target = ref.current;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
const restoreRotation = () => {
|
|
67
|
-
const saved = savedRotations.pop();
|
|
68
|
-
if (saved !== undefined) {
|
|
69
|
-
target.rotation.y = saved;
|
|
70
|
-
target.updateWorldMatrix(false, true);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
// opaque リスト用 sentinel
|
|
74
|
-
const opaquePreSentinel = new Mesh(SENTINEL_GEOMETRY, OPAQUE_MATERIAL);
|
|
75
|
-
opaquePreSentinel.frustumCulled = false;
|
|
76
|
-
opaquePreSentinel.renderOrder = -Infinity;
|
|
77
|
-
opaquePreSentinel.onBeforeRender = (_r, _s, camera) => applyRotation(camera);
|
|
78
|
-
const opaquePostSentinel = new Mesh(SENTINEL_GEOMETRY, OPAQUE_MATERIAL);
|
|
79
|
-
opaquePostSentinel.frustumCulled = false;
|
|
80
|
-
opaquePostSentinel.renderOrder = Infinity;
|
|
81
|
-
opaquePostSentinel.onBeforeRender = restoreRotation;
|
|
82
|
-
// transparent リスト用 sentinel
|
|
83
|
-
const transparentPreSentinel = new Mesh(SENTINEL_GEOMETRY, TRANSPARENT_MATERIAL);
|
|
84
|
-
transparentPreSentinel.frustumCulled = false;
|
|
85
|
-
transparentPreSentinel.renderOrder = -Infinity;
|
|
86
|
-
transparentPreSentinel.onBeforeRender = (_r, _s, camera) => applyRotation(camera);
|
|
87
|
-
const transparentPostSentinel = new Mesh(SENTINEL_GEOMETRY, TRANSPARENT_MATERIAL);
|
|
88
|
-
transparentPostSentinel.frustumCulled = false;
|
|
89
|
-
transparentPostSentinel.renderOrder = Infinity;
|
|
90
|
-
transparentPostSentinel.onBeforeRender = restoreRotation;
|
|
91
|
-
const sentinels = [
|
|
92
|
-
opaquePreSentinel,
|
|
93
|
-
opaquePostSentinel,
|
|
94
|
-
transparentPreSentinel,
|
|
95
|
-
transparentPostSentinel,
|
|
96
|
-
];
|
|
97
|
-
for (const s of sentinels)
|
|
98
|
-
target.add(s);
|
|
99
|
-
return () => {
|
|
100
|
-
for (const s of sentinels) {
|
|
101
|
-
s.onBeforeRender = () => { };
|
|
102
|
-
target.remove(s);
|
|
49
|
+
if (!target)
|
|
50
|
+
return;
|
|
51
|
+
_cameraWorldPos.setFromMatrixPosition(camera.matrixWorld);
|
|
52
|
+
_targetWorldPos.setFromMatrixPosition(target.matrixWorld);
|
|
53
|
+
// 通常はカメラ実位置を使う
|
|
54
|
+
let refPos = _cameraWorldPos;
|
|
55
|
+
const dx = _cameraWorldPos.x - _targetWorldPos.x;
|
|
56
|
+
const dz = _cameraWorldPos.z - _targetWorldPos.z;
|
|
57
|
+
if (dx * dx + dz * dz < NEAR_CAMERA_DIST_SQ) {
|
|
58
|
+
// カメラが target の真上/真下付近 → 視線方向 1m 先を仮想カメラ位置に
|
|
59
|
+
camera.getWorldDirection(_cameraForward);
|
|
60
|
+
_cameraForward.y = 0;
|
|
61
|
+
const fwdLenSq = _cameraForward.x * _cameraForward.x +
|
|
62
|
+
_cameraForward.z * _cameraForward.z;
|
|
63
|
+
if (fwdLenSq < NEAR_CAMERA_DIST_SQ) {
|
|
64
|
+
// 視線が真上/真下 → 前フレームの rotation を保持
|
|
65
|
+
return;
|
|
103
66
|
}
|
|
104
|
-
|
|
105
|
-
|
|
67
|
+
_cameraForward.normalize();
|
|
68
|
+
_virtualCameraPos
|
|
69
|
+
.copy(_cameraWorldPos)
|
|
70
|
+
.addScaledVector(_cameraForward, VIRTUAL_CAMERA_DISTANCE);
|
|
71
|
+
refPos = _virtualCameraPos;
|
|
72
|
+
}
|
|
73
|
+
const worldRotationY = getBillboardYRotation(refPos, _targetWorldPos);
|
|
74
|
+
if (target.parent) {
|
|
75
|
+
target.parent.matrixWorld.decompose(_parentPos, _parentQuat, _parentScale);
|
|
76
|
+
_euler.setFromQuaternion(_parentQuat, 'YXZ');
|
|
77
|
+
target.rotation.y = worldRotationY - _euler.y;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
target.rotation.y = worldRotationY;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
106
83
|
return ref;
|
|
107
84
|
};
|
|
108
85
|
//# sourceMappingURL=hooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/components/BillboardY/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/components/BillboardY/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EACL,KAAK,EAEL,UAAU,EACV,OAAO,GACR,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,eAAe,GAAG,IAAI,OAAO,EAAE,CAAA;AACrC,MAAM,eAAe,GAAG,IAAI,OAAO,EAAE,CAAA;AACrC,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE,CAAA;AACpC,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAE,CAAA;AACvC,MAAM,WAAW,GAAG,IAAI,UAAU,EAAE,CAAA;AACpC,MAAM,UAAU,GAAG,IAAI,OAAO,EAAE,CAAA;AAChC,MAAM,YAAY,GAAG,IAAI,OAAO,EAAE,CAAA;AAClC,MAAM,MAAM,GAAG,IAAI,KAAK,EAAE,CAAA;AAE1B;;;;;mCAKmC;AACnC,MAAM,mBAAmB,GAAG,IAAI,CAAA,CAAC,UAAU;AAE3C,gCAAgC;AAChC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAEnC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAuB,EAAE;IACpD,MAAM,GAAG,GAAG,MAAM,CAAI,IAAI,CAAC,CAAA;IAE3B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAA;QAC1B,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,eAAe,CAAC,qBAAqB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QACzD,eAAe,CAAC,qBAAqB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QAEzD,eAAe;QACf,IAAI,MAAM,GAAY,eAAe,CAAA;QAErC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAA;QAChD,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAA;QAChD,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,mBAAmB,EAAE,CAAC;YAC5C,4CAA4C;YAC5C,MAAM,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAA;YACxC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAA;YACpB,MAAM,QAAQ,GACZ,cAAc,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC;gBACnC,cAAc,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAA;YACrC,IAAI,QAAQ,GAAG,mBAAmB,EAAE,CAAC;gBACnC,iCAAiC;gBACjC,OAAM;YACR,CAAC;YACD,cAAc,CAAC,SAAS,EAAE,CAAA;YAC1B,iBAAiB;iBACd,IAAI,CAAC,eAAe,CAAC;iBACrB,eAAe,CAAC,cAAc,EAAE,uBAAuB,CAAC,CAAA;YAC3D,MAAM,GAAG,iBAAiB,CAAA;QAC5B,CAAC;QAED,MAAM,cAAc,GAAG,qBAAqB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;QAErE,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CACjC,UAAU,EACV,WAAW,EACX,YAAY,CACb,CAAA;YACD,MAAM,CAAC,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;YAC5C,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,cAAc,GAAG,MAAM,CAAC,CAAC,CAAA;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,cAAc,CAAA;QACpC,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
|