🚀 Auto-deploy: melhoria no snap e medição AR em 28/05/2026 21:16:09

This commit is contained in:
2026-05-28 21:16:09 +00:00
parent aff19146f0
commit 9b7f237afc
4 changed files with 494 additions and 18 deletions
+68 -15
View File
@@ -26,6 +26,12 @@ import { VisibilityApplier } from '@/components/three/ModelViewer';
import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms';
import { parseIFCtoThree } from '@/lib/convertIFC';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import {
xrCalibration,
pushXRModelFaceNormal,
computeXRCalibrationQuaternion,
subscribeXRCalibration,
} from '@/components/three/xrCalibrationBus';
// --- Diagnóstico XR ---
console.log('[XR] Inicializando store...');
@@ -100,8 +106,15 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
}, [sceneModel.url, sceneModel.fileName]);
const ref = useRef<THREE.Group>(null);
const calGroupRef = useRef<THREE.Group>(null);
const localFrameRef = useRef<THREE.Group>(null);
// Escuta mudanças de calibração para re-renderizar no AR
const [, setCalTick] = useState(0);
useEffect(() => subscribeXRCalibration(() => setCalTick(t => t + 1)), []);
useEffect(() => {
const g = ref.current;
const g = localFrameRef.current;
if (!g) return;
registerModelLocalGroup(sceneModel.id, g);
return () => unregisterModelLocalGroup(sceneModel.id, g);
@@ -201,24 +214,64 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
const scaleRatio = useModelStore((st) => st.scaleRatio);
const renderFactor = scaleRatio?.factor ?? 1;
const calQuatArr = sceneModel.calibrationQuat;
const isCalibratingThis = xrCalibration.modelId === sceneModel.id && xrCalibration.step !== 'idle' && xrCalibration.step !== 'done';
const calQuat = useMemo(() => {
if (isCalibratingThis) return new THREE.Quaternion();
if (!calQuatArr) return new THREE.Quaternion();
return new THREE.Quaternion(calQuatArr[0], calQuatArr[1], calQuatArr[2], calQuatArr[3]);
}, [calQuatArr, isCalibratingThis]);
if (!sceneModel.visible) return null;
if (!scene) return null;
return (
<group scale={[renderFactor, renderFactor, renderFactor]}>
<group
ref={ref}
userData={{ modelId: sceneModel.id }}
position={[
-modelInfo.center.x + fineTuning.posX,
-modelInfo.center.y + fineTuning.posY,
-modelInfo.center.z + fineTuning.posZ,
]}
rotation={[rotXRad, rotYRad, rotZRad]}
scale={[s, s, s]}
>
<primitive object={scene} />
<XRLocalModelMeasurements modelId={sceneModel.id} />
<group
scale={[renderFactor, renderFactor, renderFactor]}
onClick={(e) => {
e.stopPropagation();
// Calibração XR: captura normal da face do modelo em espaço de mundo.
if (
xrCalibration.modelId === sceneModel.id &&
(xrCalibration.step === 'await-model-1' || xrCalibration.step === 'await-model-2' || xrCalibration.step === 'await-model-3') &&
e.face && e.object
) {
const n = e.face.normal.clone();
const nm = new THREE.Matrix3().getNormalMatrix(e.object.matrixWorld);
n.applyMatrix3(nm).normalize();
const wq = new THREE.Quaternion();
(calGroupRef.current ?? e.object).getWorldQuaternion(wq);
pushXRModelFaceNormal(n, wq);
// Aplica/atualiza calibração se tivermos >= 2 pares
if (xrCalibration.pairs.length >= 2) {
const q = computeXRCalibrationQuaternion(xrCalibration.pairs);
if (q) {
useModelStore.getState().setCalibration(sceneModel.id, [q.x, q.y, q.z, q.w]);
}
}
return;
}
}}
>
{/* Translação */}
<group position={[fineTuning.posX, fineTuning.posY, fineTuning.posZ]}>
{/* Rotação e escala em volta do centro geométrico */}
<group
ref={ref}
rotation={[rotXRad, rotYRad, rotZRad]}
scale={[s, s, s]}
>
{/* Rotação de calibração */}
<group ref={calGroupRef} quaternion={calQuat}>
{/* Sistema local do modelo com pivot no centro */}
<group ref={localFrameRef} position={[-modelInfo.center.x, -modelInfo.center.y, -modelInfo.center.z]} userData={{ modelId: sceneModel.id }}>
<primitive object={scene} />
<XRLocalModelMeasurements modelId={sceneModel.id} />
</group>
</group>
</group>
</group>
</group>
);