🚀 Auto-deploy: melhoria no snap e medição AR em 30/05/2026 11:48:45

This commit is contained in:
2026-05-30 11:48:45 +00:00
parent 0ba00648e8
commit 7489b611b0
3 changed files with 61 additions and 49 deletions
+5 -1
View File
@@ -223,7 +223,11 @@ const Viewer = () => {
toast.error("WebXR não é suportado neste navegador. Use o navegador do Meta Quest 3."); toast.error("WebXR não é suportado neste navegador. Use o navegador do Meta Quest 3.");
return; return;
} }
navigate("/xr"); // Entra diretamente no AR para não perder o clique do usuário!
import('@/stores/useXRStore').then(({ xrStore }) => {
xrStore.enterAR().catch(e => console.error('[Viewer] XR enterAR error:', e));
navigate("/xr");
});
}; };
const handleEnterMeeting = () => { const handleEnterMeeting = () => {
+17 -48
View File
@@ -2,7 +2,8 @@ import { useEffect, useRef, useMemo, useCallback, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { useGLTF, Grid, Html, Line, OrbitControls, Text } from '@react-three/drei'; import { useGLTF, Grid, Html, Line, OrbitControls, Text } from '@react-three/drei';
import { XR, createXRStore, useXR, XROrigin, useXRHitTest } from '@react-three/xr'; import { XR, useXR, XROrigin, useXRHitTest } from '@react-three/xr';
import { xrStore as store } from '@/stores/useXRStore';
import * as THREE from 'three'; import * as THREE from 'three';
import { useModelStore } from '@/stores/useModelStore'; import { useModelStore } from '@/stores/useModelStore';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -45,39 +46,7 @@ if (navigator.xr) {
}); });
} }
// Lê flags de feature WebXR do localStorage (default: todas ON). // `store` is now imported from `@/stores/useXRStore`
// O usuário liga/desliga pelo HUD AR (aba WebXR) para diagnosticar qual
// feature ativa o grid de segurança do Quest. Mudanças exigem sair e re-entrar do AR.
function loadXRFeatures() {
const defaults = {
handTracking: true, planeDetection: true, hitTest: true, domOverlay: true,
anchors: true, meshDetection: true, depthSensing: true, layers: true,
bodyTracking: true, lightEstimation: true,
};
try {
const raw = localStorage.getItem('xrFeatures');
if (raw) return { ...defaults, ...JSON.parse(raw) };
} catch (err) {
console.warn('[XR] Falha ao ler xrFeatures do localStorage:', err);
}
return defaults;
}
const _xrf = loadXRFeatures();
console.log('[XR] Features:', _xrf);
const store = createXRStore({
hand: { left: true, right: true },
controller: { left: true, right: true },
handTracking: _xrf.handTracking,
planeDetection: _xrf.planeDetection,
hitTest: _xrf.hitTest,
domOverlay: _xrf.domOverlay,
anchors: _xrf.anchors,
meshDetection: _xrf.meshDetection,
depthSensing: _xrf.depthSensing,
layers: _xrf.layers,
bodyTracking: _xrf.bodyTracking,
});
// ─── XRModel ─────────────────────────────────────────── // ─── XRModel ───────────────────────────────────────────
function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').SceneModel }) { function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').SceneModel }) {
@@ -1113,6 +1082,20 @@ const XRSession = () => {
if (!model) return null; if (!model) return null;
// Auto-return to viewer if user exits AR session
useEffect(() => {
// If we are mounted, and the session is completely absent/ended (inXR is false),
// we wait a brief moment to avoid returning during initial transition, then return.
if (!inXR) {
const timer = setTimeout(() => {
if (!store.getState().session) {
navigate('/viewer');
}
}, 500);
return () => clearTimeout(timer);
}
}, [inXR, navigate]);
return ( return (
<div className="flex h-screen flex-col bg-background"> <div className="flex h-screen flex-col bg-background">
{/* Header */} {/* Header */}
@@ -1130,20 +1113,6 @@ const XRSession = () => {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{!inXR && (
<>
<Button variant="outline" size="sm" className="gap-1.5" onClick={handleDownloadMarker}>
<Download className="h-3.5 w-3.5" />
<span className="font-mono text-xs">Marcador</span>
</Button>
<Button className="gap-2 glow-primary" onClick={() => {
console.log('[XR] Botão AR clicado');
store.enterAR().catch((e) => console.error('[XR] enterAR rejeitado:', e));
}}>
Iniciar Sessão AR
</Button>
</>
)}
{inXR && ( {inXR && (
<span className="font-mono text-xs text-primary flex items-center gap-1.5"> <span className="font-mono text-xs text-primary flex items-center gap-1.5">
<span className="h-2 w-2 rounded-full bg-primary animate-pulse" /> <span className="h-2 w-2 rounded-full bg-primary animate-pulse" />
+39
View File
@@ -0,0 +1,39 @@
import { createXRStore } from '@react-three/xr';
function loadXRFeatures() {
const defaults = {
handTracking: true,
planeDetection: true,
hitTest: true,
domOverlay: true,
anchors: true,
meshDetection: true,
depthSensing: true,
layers: true,
bodyTracking: true,
lightEstimation: true,
};
try {
const raw = localStorage.getItem('xrFeatures');
if (raw) return { ...defaults, ...JSON.parse(raw) };
} catch (err) {
console.warn('[XR] Falha ao ler xrFeatures do localStorage:', err);
}
return defaults;
}
const _xrf = loadXRFeatures();
export const xrStore = createXRStore({
hand: { left: true, right: true },
controller: { left: true, right: true },
handTracking: _xrf.handTracking,
planeDetection: _xrf.planeDetection,
hitTest: _xrf.hitTest,
domOverlay: _xrf.domOverlay,
anchors: _xrf.anchors,
meshDetection: _xrf.meshDetection,
depthSensing: _xrf.depthSensing,
layers: _xrf.layers,
bodyTracking: _xrf.bodyTracking,
});