🚀 Auto-deploy: melhoria no snap e medição AR em 30/05/2026 12:24:45

This commit is contained in:
2026-05-30 12:24:45 +00:00
parent 7489b611b0
commit e4a9c8623a
+33 -13
View File
@@ -32,7 +32,7 @@ class ModelErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBounda
}
}
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Text, useGLTF, Billboard, Html } from '@react-three/drei';
import { OrbitControls, Text, useGLTF, Billboard, Html, TransformControls } from '@react-three/drei';
import * as THREE from 'three';
import { supabase } from '@/integrations/supabase/client';
import { useModelStore, type SceneModel } from '@/stores/useModelStore';
@@ -527,12 +527,13 @@ function RealMiiAvatar({
// Carregador e Escalador Inteligente da Maquete Holográfica
// Carregador e Escalador Inteligente da Maquete Holográfica
function HologramModel({ model, presentationState }: { model: SceneModel; presentationState: PresentationState }) {
function HologramModel({ model, presentationState, isPresenter, onPresentationChange }: { model: SceneModel; presentationState: PresentationState; isPresenter: boolean; onPresentationChange: (state: Partial<PresentationState>) => void }) {
const { scene } = useGLTF(model.url);
const clone = useMemo(() => scene.clone(), [scene]);
const presentationGroupRef = useRef<THREE.Group>(null);
const baseScaleRef = useRef<number>(1);
const centerRef = useRef<THREE.Vector3>(new THREE.Vector3());
const [isDragging, setIsDragging] = useState(false);
// Calcula escala de aproximadamente 1 metro para caber na mesa (apenas UMA vez no carregamento)
useEffect(() => {
@@ -589,12 +590,10 @@ function HologramModel({ model, presentationState }: { model: SceneModel; presen
// Aplica transformações de Rotação e Escala compartilhadas no grupo externo de apresentação
useFrame(() => {
if (presentationGroupRef.current && presentationState) {
if (presentationGroupRef.current && presentationState && !isDragging) {
const { rotation, scale } = presentationState;
if (rotation) {
presentationGroupRef.current.rotation.x = rotation[0];
presentationGroupRef.current.rotation.y = rotation[1];
presentationGroupRef.current.rotation.z = rotation[2];
presentationGroupRef.current.rotation.set(rotation[0], rotation[1], rotation[2]);
}
if (scale) {
presentationGroupRef.current.scale.setScalar(baseScaleRef.current * scale);
@@ -666,15 +665,35 @@ function HologramModel({ model, presentationState }: { model: SceneModel; presen
}, [calQuatArr]);
return (
// Grupo Externo de Apresentação Compartilhada
<>
{isPresenter && (
<TransformControls
object={presentationGroupRef}
mode="rotate"
size={0.6}
onDraggingChanged={(e) => setIsDragging(!!e?.value)}
onChange={() => {
if (isDragging && presentationGroupRef.current) {
onPresentationChange({
rotation: [
presentationGroupRef.current.rotation.x,
presentationGroupRef.current.rotation.y,
presentationGroupRef.current.rotation.z
]
});
}
}}
/>
)}
{/* Grupo Externo de Apresentação Compartilhada */}
<group ref={presentationGroupRef}>
{/* Grupo de Translação Fina */}
<group position={[fineTuning.posX, fineTuning.posY, fineTuning.posZ]}>
{/* Ignoramos fineTuning.posX/Y/Z para ancorar perfeitamente à mesa */}
<group>
{/* 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) */}
{/* Grupo de Centralização do Bounding Box */}
<group position={[-centerRef.current.x, -centerRef.current.y, -centerRef.current.z]}>
<primitive object={clone} />
</group>
@@ -682,11 +701,12 @@ function HologramModel({ model, presentationState }: { model: SceneModel; presen
</group>
</group>
</group>
</>
);
}
// Cenário da Sala de Reunião Clara
function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusMode }: { currentActiveModel: SceneModel | null; presentationState: PresentationState; isFocusMode: boolean }) {
function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusMode, isPresenter, onPresentationChange }: { currentActiveModel: SceneModel | null; presentationState: PresentationState; isFocusMode: boolean; isPresenter: boolean; onPresentationChange: (state: Partial<PresentationState>) => void }) {
const tableGeom = useMemo(() => {
// Geometria da mesa em U / Retangular de cantos arredondados
const shape = new THREE.Shape();
@@ -793,7 +813,7 @@ function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusM
</mesh>
}>
{currentActiveModel ? (
<HologramModel model={currentActiveModel} presentationState={presentationState} />
<HologramModel model={currentActiveModel} presentationState={presentationState} isPresenter={isPresenter} onPresentationChange={onPresentationChange} />
) : (
// Holograma Padrão Geométrico se não houver maquete carregada
<group rotation={[0, Date.now() * 0.00012, 0]}>
@@ -1717,7 +1737,7 @@ export default function MeetingRoom() {
<div className="flex-1 h-full relative">
{isWebGLSupported ? (
<Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows gl={{ localClippingEnabled: true }}>
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} />
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} isPresenter={isPresenter} onPresentationChange={handlePresentationChange} />
{/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
{!isFocusMode && Object.keys(mergedPeers).map((chairKey) => {