diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 0d5bd1a..de6f020 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -10,26 +10,11 @@ import { ArrowLeft, Move, RotateCw, QrCode, Download, Crosshair } from 'lucide-r import { Button } from '@/components/ui/button'; import { generateMarkerDownloadURL } from '@/lib/trackingMarker'; -// --- XR Store singleton --- -let xrStoreInstance: ReturnType | null = null; - -function getOrCreateXRStore() { - if (xrStoreInstance) return xrStoreInstance; - - // Only emulate when on localhost AND no native XR support - const shouldEmulate = - typeof window !== 'undefined' && - window.location.hostname === 'localhost' && - !navigator.xr; - - xrStoreInstance = createXRStore({ - emulate: shouldEmulate ? 'metaQuest3' : false, - hand: { left: true, right: true }, - controller: { left: true, right: true }, - }); - - return xrStoreInstance; -} +// --- XR Store at MODULE level (required by Quest 3 browser) --- +const store = createXRStore({ + hand: { left: true, right: true }, + controller: { left: true, right: true }, +}); // ─── XRModel ─────────────────────────────────────────── function XRModel({ url }: { url: string }) { @@ -129,8 +114,8 @@ function ControllerFineTuning() { const posStep = 0.001; const rotStep = 0.1; const deadzone = 0.15; - const store = useModelStore.getState(); - const ft = { ...store.fineTuning }; + const modelStore = useModelStore.getState(); + const ft = { ...modelStore.fineTuning }; if (leftAxes) { if (Math.abs(leftAxes[0]) > deadzone) ft.posX += leftAxes[0] * posStep; @@ -150,47 +135,10 @@ function ControllerFineTuning() { // ─── ImageTrackingAnchor ─────────────────────────────── function ImageTrackingAnchor({ children }: { children: React.ReactNode }) { const groupRef = useRef(null); - const session = useXR((s) => s.session); - const { gl } = useThree(); - const { setAnchorMode } = useModelStore(); - const trackingActive = useRef(false); - useFrame((_, __, frame: any) => { - if (!frame || !session || !groupRef.current) return; - - try { - const results = frame.getImageTrackingResults?.(); - if (results && results.length > 0) { - for (const result of results) { - if (result.trackingState === 'tracked' || result.trackingState === 'emulated') { - const refSpace = gl.xr.getReferenceSpace(); - if (!refSpace) continue; - - const pose = frame.getPose(result.imageSpace, refSpace); - if (pose) { - const pos = pose.transform.position; - const ori = pose.transform.orientation; - - groupRef.current.position.set(pos.x, pos.y, pos.z); - groupRef.current.quaternion.set(ori.x, ori.y, ori.z, ori.w); - - if (!trackingActive.current) { - trackingActive.current = true; - setAnchorMode('tracking'); - toast.success('QR Code detectado — Modelo ancorado!'); - } - return; - } - } - } - } - } catch { - // getImageTrackingResults not available - } - - if (trackingActive.current) return; - - // Manual fallback — place 1.5m in front + useFrame(() => { + if (!groupRef.current) return; + // Place model 1.5m in front of the user groupRef.current.position.set(0, 0, -1.5); groupRef.current.quaternion.identity(); }); @@ -203,7 +151,6 @@ const XRSession = () => { const navigate = useNavigate(); const { model, anchorMode, setAnchorMode } = useModelStore(); const [inXR, setInXR] = useState(false); - const [store] = useState(() => getOrCreateXRStore()); useEffect(() => { if (!model) { @@ -211,23 +158,22 @@ const XRSession = () => { } }, [model, navigate]); - const handleEnterAR = useCallback(async () => { - try { - const session = await store.enterAR(); - if (session) { + // Listen for XR session state changes + useEffect(() => { + const unsubscribe = store.subscribe((state) => { + const session = state.session; + if (session && !inXR) { + setInXR(true); + setAnchorMode('manual'); + toast.success('Sessão AR iniciada!'); session.addEventListener('end', () => { setInXR(false); setAnchorMode('manual'); }); - setInXR(true); - setAnchorMode('manual'); - toast.success('Sessão XR iniciada!'); } - } catch (err: any) { - console.error('XR error:', err); - toast.error(`Falha ao iniciar XR: ${err.message}`); - } - }, [store, setAnchorMode]); + }); + return unsubscribe; + }, [inXR, setAnchorMode]); const handleDownloadMarker = useCallback(async () => { const url = await generateMarkerDownloadURL(); @@ -261,7 +207,8 @@ const XRSession = () => { Marcador QR - @@ -318,12 +265,6 @@ const XRSession = () => { {/* Floating HUD overlay */}
-
- - - {anchorMode === 'tracking' ? '● QR Detectado' : '○ Buscando QR…'} - -