🚀 Auto-deploy: melhoria no snap e medição AR em 24/05/2026 12:38:19

This commit is contained in:
2026-05-24 12:38:19 +00:00
parent 6046d655df
commit 377346893e
2 changed files with 31 additions and 11 deletions
+20 -11
View File
@@ -53,11 +53,15 @@ const WristToggle = forwardRef<THREE.Group, { open: boolean; onToggle: () => voi
); );
export function XRHudInWorld(props: XRHudInWorldProps) { export function XRHudInWorld(props: XRHudInWorldProps) {
const { camera } = useThree(); const { camera, gl } = useThree();
const leftCtrl = useXRInputSourceState('controller', 'left'); const leftCtrl = useXRInputSourceState('controller', 'left');
const rightCtrl = useXRInputSourceState('controller', 'right'); const rightCtrl = useXRInputSourceState('controller', 'right');
const [open, setOpen] = useState(true);
const [tab, setTab] = useState<Tab>('scene'); const open = useModelStore((s) => s.xrHudOpen);
const setOpen = useModelStore((s) => s.setXrHudOpen);
const tab = useModelStore((s) => s.xrHudTab) as Tab;
const setTab = useModelStore((s) => s.setXrHudTab) as unknown as (t: Tab) => void;
/** When pinned, the panel stays fixed in world space and does NOT follow the head. */ /** When pinned, the panel stays fixed in world space and does NOT follow the head. */
const [pinned, setPinned] = useState(true); const [pinned, setPinned] = useState(true);
/** While true, the panel is glued to the right controller (drag mode). */ /** While true, the panel is glued to the right controller (drag mode). */
@@ -82,7 +86,7 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
if (gp) { if (gp) {
const aBtn = gp.buttons[4]; const aBtn = gp.buttons[4];
const pressed = !!aBtn?.pressed; const pressed = !!aBtn?.pressed;
if (pressed && !lastABtn.current) setOpen((v) => !v); if (pressed && !lastABtn.current) setOpen(!open);
lastABtn.current = pressed; lastABtn.current = pressed;
} }
@@ -150,11 +154,11 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
} }
} }
// Head-locked fallback toggle — always visible in lower-left of FOV, // Head-locked fallback toggle — always visible in lower-right of FOV,
// so the user can reopen the menu even without controllers or if the // so the user can reopen the menu even without controllers or if the
// panel is positioned out of view. // panel is positioned out of view.
if (headLockRef.current) { if (headLockRef.current) {
const offset = new THREE.Vector3(-0.18, -0.18, -0.5).applyQuaternion(camera.quaternion); const offset = new THREE.Vector3(0.18, -0.18, -0.5).applyQuaternion(camera.quaternion);
headLockRef.current.position.copy(camera.position).add(offset); headLockRef.current.position.copy(camera.position).add(offset);
headLockRef.current.quaternion.copy(camera.quaternion); headLockRef.current.quaternion.copy(camera.quaternion);
} }
@@ -165,16 +169,21 @@ export function XRHudInWorld(props: XRHudInWorldProps) {
return ( return (
<> <>
<WristToggle ref={wristRef} open={open} onToggle={() => setOpen((v) => !v)} /> <WristToggle ref={wristRef} open={open} onToggle={() => setOpen(!open)} />
{/* Head-locked fallback: always-visible quick controls in lower-left of FOV */} {/* Head-locked fallback: always-visible quick controls in lower-right of FOV */}
<group ref={headLockRef} renderOrder={999}> <group ref={headLockRef} renderOrder={999}>
<XR3DButton position={[0, 0.018, 0]} size={[0.07, 0.022]} <XR3DButton position={[0, 0.026, 0]} size={[0.07, 0.022]}
label={open ? '✕ Menu' : '☰ Menu'} active={open} label={open ? '✕ Menu' : '☰ Menu'} active={open}
onClick={() => { setOpen((v) => !v); reanchorRequested.current = true; }} onClick={() => { setOpen(!open); reanchorRequested.current = true; }}
fontSize={0.0085} /> fontSize={0.0085} />
<XR3DButton position={[0, -0.008, 0]} size={[0.07, 0.022]} <XR3DButton position={[0, 0, 0]} size={[0.07, 0.022]}
label={showGrid ? 'Grid ON' : 'Grid OFF'} active={showGrid} label={showGrid ? 'Grid ON' : 'Grid OFF'} active={showGrid}
onClick={() => setShowGrid(!showGrid)} fontSize={0.0085} /> onClick={() => setShowGrid(!showGrid)} fontSize={0.0085} />
<XR3DButton position={[0, -0.026, 0]} size={[0.07, 0.022]}
label="✕ Sair AR" active={false}
onClick={() => {
gl.xr.getSession()?.end();
}} fontSize={0.0085} />
<RecIndicator /> <RecIndicator />
</group> </group>
{open && ( {open && (
+11
View File
@@ -299,6 +299,12 @@ interface ModelStore {
xrFeatures: XRFeatureFlags; xrFeatures: XRFeatureFlags;
setXRFeature: (key: keyof XRFeatureFlags, value: boolean) => void; setXRFeature: (key: keyof XRFeatureFlags, value: boolean) => void;
resetXRFeatures: () => void; resetXRFeatures: () => void;
// ── WebXR HUD states persistent cache ────
xrHudOpen: boolean;
setXrHudOpen: (open: boolean) => void;
xrHudTab: string;
setXrHudTab: (tab: string) => void;
} }
/** Sync top-level legacy `model` / `fineTuning` from active SceneModel. */ /** Sync top-level legacy `model` / `fineTuning` from active SceneModel. */
@@ -673,4 +679,9 @@ export const useModelStore = create<ModelStore>((set, get) => ({
saveXRFeatures(next); saveXRFeatures(next);
set({ xrFeatures: next }); set({ xrFeatures: next });
}, },
xrHudOpen: true,
setXrHudOpen: (open) => set({ xrHudOpen: open }),
xrHudTab: 'scene',
setXrHudTab: (tab) => set({ xrHudTab: tab }),
})); }));