🚀 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 * 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<THREE.Group>(null);
const presentationGroupRef = useRef<THREE.Group>(null);
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)
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 (
<group ref={groupRef}>
<primitive object={clone} />
// Grupo Externo de Apresentação Compartilhada
<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>
);
}
// 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 (
<group>
{/* Luzes da Sala / Foco */}