diff --git a/src/pages/MeetingRoom.tsx b/src/pages/MeetingRoom.tsx index 754e277..d3b9d66 100644 --- a/src/pages/MeetingRoom.tsx +++ b/src/pages/MeetingRoom.tsx @@ -35,7 +35,7 @@ import { Canvas, useFrame } from '@react-three/fiber'; import { OrbitControls, Text, useGLTF } from '@react-three/drei'; import * as THREE from 'three'; 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 { Input } from '@/components/ui/input'; 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 -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 clone = useMemo(() => scene.clone(), [scene]); - const groupRef = useRef(null); + const presentationGroupRef = useRef(null); const baseScaleRef = useRef(1); - const centerOffsetRef = useRef(new THREE.Vector3()); + const centerRef = useRef(new THREE.Vector3()); // Calcula escala de aproximadamente 1 metro para caber na mesa (apenas UMA vez no carregamento) useEffect(() => { - if (!groupRef.current) return; - - // Salva transformações antigas + if (!clone) return; + + // Reseta rotação e escala para obter o bounding box original limpo const oldRot = clone.rotation.clone(); const oldScale = clone.scale.clone(); - - // Reseta temporariamente para obter o bounding box original clone.rotation.set(0, 0, 0); clone.scale.setScalar(1); @@ -408,10 +407,7 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p const center = new THREE.Vector3(); box.getCenter(center); - centerOffsetRef.current.copy(center).multiplyScalar(-scaleFactor); - centerOffsetRef.current.y += 0.02; - - clone.position.copy(centerOffsetRef.current); + centerRef.current.copy(center); } // Restaura @@ -419,17 +415,40 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p clone.scale.copy(oldScale); }, [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(() => { - if (groupRef.current && presentationState) { + if (presentationGroupRef.current && presentationState) { const { rotation, scale } = presentationState; if (rotation) { - groupRef.current.rotation.x = rotation[0]; - groupRef.current.rotation.y = rotation[1]; - groupRef.current.rotation.z = rotation[2]; + presentationGroupRef.current.rotation.x = rotation[0]; + presentationGroupRef.current.rotation.y = rotation[1]; + presentationGroupRef.current.rotation.z = rotation[2]; } 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 === '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) => { if (obj instanceof THREE.Mesh) { @@ -473,18 +500,44 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p }); } }); - }, [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 ( - - + // Grupo Externo de Apresentação Compartilhada + + {/* Grupo de Translação Fina */} + + {/* Grupo de Rotação Fina e Escala Fina */} + + {/* Grupo de Calibração */} + + {/* Grupo de Centralização do Bounding Box (para girar em torno do seu centro geométrico) */} + + + + + + ); } // 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 ( {/* Luzes da Sala / Foco */}