🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 01:15:05
This commit is contained in:
+76
-23
@@ -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;
|
||||
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 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 */}
|
||||
|
||||
Reference in New Issue
Block a user