Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-24 19:22:23 +00:00
parent 5bea0ff9ee
commit dda94bd107
+24 -9
View File
@@ -1,6 +1,7 @@
import { useRef, useState, forwardRef } from 'react';
import { useFrame, useThree } from '@react-three/fiber';
import { useXRInputSourceState } from '@react-three/xr';
import { useXRInputSourceState, useXR } from '@react-three/xr';
import { useNavigate } from 'react-router-dom';
import * as THREE from 'three';
import { Text } from '@react-three/drei';
import { XR3DButton, XR3DPanel } from './XRPanel3D';
@@ -56,7 +57,11 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
const { camera } = useThree();
const leftCtrl = useXRInputSourceState('controller', 'left');
const rightCtrl = useXRInputSourceState('controller', 'right');
const xrSession = useXR((s) => s.session);
const navigate = useNavigate();
const [open, setOpen] = useState(true);
/** Visibility of the small head-locked floating button group (toggled by X button). */
const [hudVisible, setHudVisible] = 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);
@@ -77,12 +82,12 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
const dragInitialized = useRef(false);
useFrame(() => {
// Toggle via left A button (Quest Touch buttons[4])
// Toggle the head-locked 3-button group via left X button (Quest Touch buttons[4])
const gp = leftCtrl?.inputSource?.gamepad;
if (gp) {
const aBtn = gp.buttons[4];
const pressed = !!aBtn?.pressed;
if (pressed && !lastABtn.current) setOpen((v) => !v);
const xBtn = gp.buttons[4];
const pressed = !!xBtn?.pressed;
if (pressed && !lastABtn.current) setHudVisible((v) => !v);
lastABtn.current = pressed;
}
@@ -166,15 +171,25 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
return (
<>
<WristToggle ref={wristRef} open={open} onToggle={() => setOpen((v) => !v)} />
{/* Head-locked fallback: always-visible quick controls in lower-left of FOV */}
<group ref={headLockRef} renderOrder={999}>
<XR3DButton position={[0, 0.018, 0]} size={[0.07, 0.022]}
{/* Head-locked fallback: floating quick-controls in lower-left of FOV.
Toggled on/off by the left controller X button. */}
<group ref={headLockRef} renderOrder={999} visible={hudVisible}>
<XR3DButton position={[0, 0.030, 0]} size={[0.07, 0.022]}
label={open ? '✕ Menu' : '☰ Menu'} active={open}
onClick={() => { setOpen((v) => !v); reanchorRequested.current = true; }}
fontSize={0.0085} />
<XR3DButton position={[0, -0.008, 0]} size={[0.07, 0.022]}
<XR3DButton position={[0, 0.004, 0]} size={[0.07, 0.022]}
label={showGrid ? 'Grid ON' : 'Grid OFF'} active={showGrid}
onClick={() => setShowGrid(!showGrid)} fontSize={0.0085} />
<XR3DButton position={[0, -0.022, 0]} size={[0.07, 0.022]}
label="⏏ Sair AR"
onClick={() => {
// End the XR session while preserving placement/state in the store,
// then return to the Viewer. Re-entering AR restores the same state.
try { xrSession?.end(); } catch (e) { console.warn('[XR] session.end failed', e); }
setTimeout(() => navigate('/viewer'), 50);
}}
fontSize={0.0085} />
<RecIndicator />
</group>
{open && (