🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 01:15:05

This commit is contained in:
2026-05-25 01:15:05 +00:00
parent 408eba84e0
commit cf4a317439
+77 -24
View File
@@ -35,7 +35,7 @@ import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Text, useGLTF } from '@react-three/drei'; import { OrbitControls, Text, useGLTF } from '@react-three/drei';
import * as THREE from 'three'; import * as THREE from 'three';
import { supabase } from '@/integrations/supabase/client'; import { supabase } from '@/integrations/supabase/client';
import { useModelStore } from '@/stores/useModelStore'; import { useModelStore, type SceneModel } from '@/stores/useModelStore';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
@@ -377,22 +377,21 @@ function MiiAvatar({ avatarId, username, isTalking, scale = 1, ...props }: { ava
} }
// Carregador e Escalador Inteligente da Maquete Holográfica // Carregador e Escalador Inteligente da Maquete Holográfica
function HologramModel({ model, presentationState }: { model: { url: string }; presentationState: PresentationState }) { // Carregador e Escalador Inteligente da Maquete Holográfica
function HologramModel({ model, presentationState }: { model: SceneModel; presentationState: PresentationState }) {
const { scene } = useGLTF(model.url); const { scene } = useGLTF(model.url);
const clone = useMemo(() => scene.clone(), [scene]); const clone = useMemo(() => scene.clone(), [scene]);
const groupRef = useRef<THREE.Group>(null); const presentationGroupRef = useRef<THREE.Group>(null);
const baseScaleRef = useRef<number>(1); const baseScaleRef = useRef<number>(1);
const centerOffsetRef = useRef<THREE.Vector3>(new THREE.Vector3()); const centerRef = useRef<THREE.Vector3>(new THREE.Vector3());
// Calcula escala de aproximadamente 1 metro para caber na mesa (apenas UMA vez no carregamento) // Calcula escala de aproximadamente 1 metro para caber na mesa (apenas UMA vez no carregamento)
useEffect(() => { useEffect(() => {
if (!groupRef.current) return; if (!clone) return;
// Salva transformações antigas // Reseta rotação e escala para obter o bounding box original limpo
const oldRot = clone.rotation.clone(); const oldRot = clone.rotation.clone();
const oldScale = clone.scale.clone(); const oldScale = clone.scale.clone();
// Reseta temporariamente para obter o bounding box original
clone.rotation.set(0, 0, 0); clone.rotation.set(0, 0, 0);
clone.scale.setScalar(1); clone.scale.setScalar(1);
@@ -408,10 +407,7 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
const center = new THREE.Vector3(); const center = new THREE.Vector3();
box.getCenter(center); box.getCenter(center);
centerOffsetRef.current.copy(center).multiplyScalar(-scaleFactor); centerRef.current.copy(center);
centerOffsetRef.current.y += 0.02;
clone.position.copy(centerOffsetRef.current);
} }
// Restaura // Restaura
@@ -419,17 +415,40 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
clone.scale.copy(oldScale); clone.scale.copy(oldScale);
}, [clone]); }, [clone]);
// Aplica transformações de Rotação e Escala compartilhadas // Aplica cor do modelo nos materiais do clone
useEffect(() => {
if (!clone || !model.color) return;
clone.traverse((child) => {
if (child instanceof THREE.Mesh) {
// Clone do material para evitar afetar cache global do R3F
if (Array.isArray(child.material)) {
child.material = child.material.map(m => m.clone());
} else {
child.material = child.material.clone();
}
const materials = Array.isArray(child.material) ? child.material : [child.material];
materials.forEach((mat: THREE.Material) => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.color.set(model.color);
mat.needsUpdate = true;
}
});
}
});
}, [clone, model.color]);
// Aplica transformações de Rotação e Escala compartilhadas no grupo externo de apresentação
useFrame(() => { useFrame(() => {
if (groupRef.current && presentationState) { if (presentationGroupRef.current && presentationState) {
const { rotation, scale } = presentationState; const { rotation, scale } = presentationState;
if (rotation) { if (rotation) {
groupRef.current.rotation.x = rotation[0]; presentationGroupRef.current.rotation.x = rotation[0];
groupRef.current.rotation.y = rotation[1]; presentationGroupRef.current.rotation.y = rotation[1];
groupRef.current.rotation.z = rotation[2]; presentationGroupRef.current.rotation.z = rotation[2];
} }
if (scale) { if (scale) {
groupRef.current.scale.setScalar(baseScaleRef.current * scale); presentationGroupRef.current.scale.setScalar(baseScaleRef.current * scale);
} }
} }
}); });
@@ -459,7 +478,15 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
else if (sectionAxis === 'y') normal.set(0, sectionInvert ? -1 : 1, 0); else if (sectionAxis === 'y') normal.set(0, sectionInvert ? -1 : 1, 0);
else if (sectionAxis === 'z') normal.set(0, 0, sectionInvert ? -1 : 1); else if (sectionAxis === 'z') normal.set(0, 0, sectionInvert ? -1 : 1);
const plane = new THREE.Plane(normal, sectionLevel); // Ajusta a distância baseado no bounding box original e no nível do corte
const box = new THREE.Box3().setFromObject(clone);
const size = new THREE.Vector3();
box.getSize(size);
const minVal = sectionAxis === 'x' ? box.min.x : sectionAxis === 'y' ? box.min.y : box.min.z;
const maxVal = sectionAxis === 'x' ? box.max.x : sectionAxis === 'y' ? box.max.y : box.max.z;
const distance = minVal + (maxVal - minVal) * sectionLevel;
const plane = new THREE.Plane(normal, -distance);
clone.traverse((obj) => { clone.traverse((obj) => {
if (obj instanceof THREE.Mesh) { if (obj instanceof THREE.Mesh) {
@@ -473,18 +500,44 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
}); });
} }
}); });
}, [clone, presentationState]); }, [clone, presentationState]);
// Translações, Rotações finas e Escala fina nativas da maquete vindas do Viewer
const fineTuning = model.fineTuning || { posX: 0, posY: 0, posZ: 0, rotX: 0, rotY: 0, rotZ: 0, scale: 1 };
const rotXRad = (fineTuning.rotX * Math.PI) / 180;
const rotYRad = (fineTuning.rotY * Math.PI) / 180;
const rotZRad = (fineTuning.rotZ * Math.PI) / 180;
const s = fineTuning.scale ?? 1;
// Quaternion de calibração nativo da maquete
const calQuatArr = model.calibrationQuat;
const calQuat = useMemo(() => {
if (!calQuatArr) return new THREE.Quaternion();
return new THREE.Quaternion(calQuatArr[0], calQuatArr[1], calQuatArr[2], calQuatArr[3]);
}, [calQuatArr]);
return ( return (
<group ref={groupRef}> // Grupo Externo de Apresentação Compartilhada
<primitive object={clone} /> <group ref={presentationGroupRef}>
{/* Grupo de Translação Fina */}
<group position={[fineTuning.posX, fineTuning.posY, fineTuning.posZ]}>
{/* Grupo de Rotação Fina e Escala Fina */}
<group rotation={[rotXRad, rotYRad, rotZRad]} scale={[s, s, s]}>
{/* Grupo de Calibração */}
<group quaternion={calQuat}>
{/* Grupo de Centralização do Bounding Box (para girar em torno do seu centro geométrico) */}
<group position={[-centerRef.current.x, -centerRef.current.y, -centerRef.current.z]}>
<primitive object={clone} />
</group>
</group>
</group>
</group>
</group> </group>
); );
} }
// Cenário da Sala de Reunião Clara // Cenário da Sala de Reunião Clara
function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusMode }: { currentActiveModel: { url: string; fileName: string } | null; presentationState: PresentationState; isFocusMode: boolean }) { function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusMode }: { currentActiveModel: SceneModel | null; presentationState: PresentationState; isFocusMode: boolean }) {
return ( return (
<group> <group>
{/* Luzes da Sala / Foco */} {/* Luzes da Sala / Foco */}