🚀 Auto-deploy: melhoria no snap e medição AR em 24/05/2026 12:38:19
This commit is contained in:
@@ -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 && (
|
||||||
|
|||||||
@@ -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 }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user