Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-14 18:27:18 +00:00
parent 1e19cfca59
commit e9227f51bf
+32 -6
View File
@@ -53,6 +53,10 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
const leftCtrl = useXRInputSourceState('controller', 'left');
const [open, setOpen] = useState(true);
const [tab, setTab] = useState<Tab>('scene');
/** When pinned, the panel stays fixed in world space and does NOT follow the head. */
const [pinned, setPinned] = useState(true);
/** Forces re-anchor on next frame (used by "trazer" / unpin→pin). */
const reanchorRequested = useRef(true);
const panelRef = useRef<THREE.Group>(null);
const wristRef = useRef<THREE.Group>(null);
@@ -70,14 +74,29 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
lastABtn.current = pressed;
}
// Floating panel ~70cm in front of head, yaw-locked, damped
// Floating panel:
// • If pinned and already anchored once → don't move it (fixed in space).
// • If unpinned → continuously follow head with damping.
// • On (re)anchor request → snap target once and stop.
if (panelRef.current && open) {
const fwd = new THREE.Vector3(0, -0.05, -0.7).applyQuaternion(camera.quaternion);
targetPos.current.copy(camera.position).add(fwd);
const wantPos = new THREE.Vector3().copy(camera.position).add(fwd);
const euler = new THREE.Euler().setFromQuaternion(camera.quaternion, 'YXZ');
targetQuat.current.setFromEuler(new THREE.Euler(0, euler.y, 0, 'YXZ'));
panelRef.current.position.lerp(targetPos.current, 0.12);
panelRef.current.quaternion.slerp(targetQuat.current, 0.18);
const wantQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, euler.y, 0, 'YXZ'));
if (reanchorRequested.current) {
targetPos.current.copy(wantPos);
targetQuat.current.copy(wantQuat);
panelRef.current.position.copy(wantPos);
panelRef.current.quaternion.copy(wantQuat);
reanchorRequested.current = false;
} else if (!pinned) {
targetPos.current.copy(wantPos);
targetQuat.current.copy(wantQuat);
panelRef.current.position.lerp(targetPos.current, 0.12);
panelRef.current.quaternion.slerp(targetQuat.current, 0.18);
}
// pinned + already anchored → leave panel where it is.
}
// Wrist toggle follows left controller
@@ -102,7 +121,14 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
<WristToggle ref={wristRef} open={open} onToggle={() => setOpen((v) => !v)} />
{open && (
<group ref={panelRef}>
<FloatingPanel tab={tab} setTab={setTab} {...props} />
<FloatingPanel
tab={tab}
setTab={setTab}
pinned={pinned}
onTogglePin={() => setPinned((v) => !v)}
onRecenter={() => { reanchorRequested.current = true; }}
{...props}
/>
</group>
)}
</>