🚀 Auto-deploy: melhoria no snap e medição AR em 30/05/2026 11:48:45
This commit is contained in:
@@ -223,7 +223,11 @@ const Viewer = () => {
|
||||
toast.error("WebXR não é suportado neste navegador. Use o navegador do Meta Quest 3.");
|
||||
return;
|
||||
}
|
||||
// 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 = () => {
|
||||
|
||||
+17
-48
@@ -2,7 +2,8 @@ import { useEffect, useRef, useMemo, useCallback, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
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 { useModelStore } from '@/stores/useModelStore';
|
||||
import { toast } from 'sonner';
|
||||
@@ -45,39 +46,7 @@ if (navigator.xr) {
|
||||
});
|
||||
}
|
||||
|
||||
// Lê flags de feature WebXR do localStorage (default: todas ON).
|
||||
// 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,
|
||||
});
|
||||
// `store` is now imported from `@/stores/useXRStore`
|
||||
|
||||
// ─── XRModel ───────────────────────────────────────────
|
||||
function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').SceneModel }) {
|
||||
@@ -1113,6 +1082,20 @@ const XRSession = () => {
|
||||
|
||||
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 (
|
||||
<div className="flex h-screen flex-col bg-background">
|
||||
{/* Header */}
|
||||
@@ -1130,20 +1113,6 @@ const XRSession = () => {
|
||||
</div>
|
||||
|
||||
<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 && (
|
||||
<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" />
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
Reference in New Issue
Block a user