🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 00:07:59

This commit is contained in:
2026-05-25 00:07:59 +00:00
parent de8558b81c
commit f38bf58bd2
+263 -209
View File
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState, useMemo } from 'react';
import { useEffect, useRef, useState, useMemo, Suspense } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { OrbitControls, Text, Line } from '@react-three/drei';
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';
@@ -10,7 +10,7 @@ import { Input } from '@/components/ui/input';
import { toast } from 'sonner';
import {
Users, MessageSquare, Mic, MicOff, Home, Send,
ArrowRight, Check, ChevronLeft, ChevronRight, Share2, LogOut
ArrowRight, ChevronLeft, ChevronRight, Share2, LogOut
} from 'lucide-react';
// --- Configurações da Mesa de Reunião ---
@@ -26,42 +26,60 @@ const CHAIRS = [
{ id: '8', name: 'Assento 8', pos: [-1.2, 0.95, 0.6], lookAt: [0, 0.95, 0] },
];
// 10 Modelos de Avatar Futurista (representações no Lobby)
// 10 Modelos de Avatar estilo Nintendo Wii (Mii) com características humanas
const AVATAR_PRESETS = [
{ id: 1, name: 'Cyber Helmet', desc: 'Elmo octaédrico com visor ciano', color: '#0ea5e9' },
{ id: 2, name: 'Gold Mask', desc: 'Máscara poliédrica dourada', color: '#eab308' },
{ id: 3, name: 'Neon Ring', desc: 'Esfera com órbita luminosa', color: '#ec4899' },
{ id: 4, name: 'Retro Bot', desc: 'Unidade cúbica com visor esmeralda', color: '#10b981' },
{ id: 5, name: 'Crystal Prism', desc: 'Estrutura geométrica ametista', color: '#a855f7' },
{ id: 6, name: 'Vector Visor', desc: 'Busto metálico com LED vermelho', color: '#ef4444' },
{ id: 7, name: 'Quantum Core', desc: 'Núcleo de energia flutuante', color: '#00f5ff' },
{ id: 8, name: 'Obsidian Shell', desc: 'Casca geométrica com núcleo laranja', color: '#f97316' },
{ id: 9, name: 'Helix Guardian', desc: 'Espiral cósmica dupla', color: '#3b82f6' },
{ id: 10, name: 'Carbon Apex', desc: 'Estrutura furtiva em cinza grafite', color: '#64748b' },
{ id: 1, name: 'Mii Cyber', desc: 'Pele clara com óculos vermelhos e cabelo curto', color: '#ef4444' },
{ id: 2, name: 'Mii Classic', desc: 'Pele média com cabelo castanho clássico', color: '#b45309' },
{ id: 3, name: 'Mii Goldie', desc: 'Cabelo loiro longo com olhos azuis expressivos', color: '#eab308' },
{ id: 4, name: 'Mii Sporty', desc: 'Boné azul virado para trás e sorriso aberto', color: '#3b82f6' },
{ id: 5, name: 'Mii Ginger', desc: 'Cabelo ruivo longo ondulado e expressivo', color: '#f97316' },
{ id: 6, name: 'Mii Sleek', desc: 'Pele parda com cabelo curto liso lateral', color: '#10b981' },
{ id: 7, name: 'Mii Beard', desc: 'Careca estilosa com barba e bigode', color: '#78350f' },
{ id: 8, name: 'Mii Sun', desc: 'Cabelo raspado e óculos escuros pretos', color: '#1e293b' },
{ id: 9, name: 'Mii Synth', desc: 'Cabelo roxo ondulado moderno', color: '#a855f7' },
{ id: 10, name: 'Mii Straw', desc: 'Chapéu de palha do campo clássico', color: '#ca8a04' },
];
// --- Subcomponentes 3D do Cenário ---
// Cores de pele padrão para os Miis
const SKIN_COLORS = ['#fed7aa', '#fdba74', '#f59e0b', '#d97706', '#9a3412', '#ffedd5'];
// Avatar Tridimensional Renderizado
function ThreeAvatar({ avatarId, color, isTalking, username, ...props }: { avatarId: number; color: string; isTalking?: boolean; username: string } & any) {
// --- Subcomponentes 3D ---
// Avatar estilo Mii (Nintendo Wii)
function MiiAvatar({ avatarId, username, isTalking, ...props }: { avatarId: number; username: string; isTalking?: boolean } & any) {
const groupRef = useRef<THREE.Group>(null);
const colorObj = useMemo(() => new THREE.Color(color), [color]);
// Parâmetros do rosto e cabelo com base no ID selecionado
const mii = useMemo(() => {
const skin = SKIN_COLORS[(avatarId - 1) % SKIN_COLORS.length];
return {
skin,
hairColor: avatarId === 3 ? '#eab308' : avatarId === 9 ? '#a855f7' : avatarId === 5 ? '#f97316' : '#27272a',
hasGlasses: avatarId === 1 || avatarId === 8,
glassesColor: avatarId === 8 ? '#18181b' : '#ef4444',
hasCap: avatarId === 4,
capColor: '#3b82f6',
hasHat: avatarId === 10,
hatColor: '#b45309',
hairStyle: avatarId === 7 ? 'bald' : (avatarId % 3 === 0 ? 'long' : 'short'),
beard: avatarId === 7,
};
}, [avatarId]);
useFrame((state) => {
if (groupRef.current) {
// Pequena oscilação flutuante sutil
groupRef.current.position.y = props.position[1] + Math.sin(state.clock.getElapsedTime() * 2 + avatarId) * 0.015;
// Pequena flutuação para dar vida
groupRef.current.position.y = props.position[1] + Math.sin(state.clock.getElapsedTime() * 2 + avatarId) * 0.012;
}
});
return (
<group ref={groupRef} {...props}>
{/* Nome do Participante acima da cabeça */}
{/* Balão com Nome acima da cabeça */}
<group position={[0, 0.28, 0]}>
{/* Painel do nome */}
<mesh position={[0, 0, -0.001]}>
<planeGeometry args={[0.22, 0.05]} />
<meshBasicMaterial color="#0f172a" transparent opacity={0.8} />
<meshBasicMaterial color="#0f172a" transparent opacity={0.7} />
</mesh>
<Text fontSize={0.024} color="#ffffff" anchorX="center" anchorY="middle" fontStyle="bold">
{username}
@@ -76,142 +94,136 @@ function ThreeAvatar({ avatarId, color, isTalking, username, ...props }: { avata
</mesh>
)}
{/* Renderizadores dos avatares baseados em presets geométricos */}
{avatarId === 1 && (
// Cyber Helmet
<mesh position={[0, 0.1, 0]}>
<octahedronGeometry args={[0.1]} />
<meshStandardMaterial color="#1e293b" roughness={0.2} metalness={0.8} />
<mesh position={[0, 0.02, 0.06]}>
<boxGeometry args={[0.12, 0.025, 0.02]} />
<meshBasicMaterial color={colorObj} />
</mesh>
{/* CABEÇA MII (Esfera ovalada de pele) */}
<mesh position={[0, 0.1, 0]} scale={[1, 1.15, 1]}>
<sphereGeometry args={[0.08, 32, 32]} />
<meshStandardMaterial color={mii.skin} roughness={0.6} />
</mesh>
{/* CABELO */}
{mii.hairStyle === 'short' && (
<mesh position={[0, 0.15, -0.015]} scale={[1.05, 0.8, 1.05]}>
<sphereGeometry args={[0.08, 16, 16]} />
<meshStandardMaterial color={mii.hairColor} roughness={0.8} />
</mesh>
)}
{avatarId === 2 && (
// Gold Mask
<mesh position={[0, 0.1, 0]} rotation={[0, 0, 0]}>
<dodecahedronGeometry args={[0.095]} />
<meshStandardMaterial color="#b45309" roughness={0.1} metalness={0.9} />
<mesh position={[0, 0, 0.06]}>
<coneGeometry args={[0.03, 0.06, 4]} rotation={[Math.PI / 2, 0, 0]} />
<meshStandardMaterial color="#f59e0b" roughness={0.2} metalness={0.8} />
{mii.hairStyle === 'long' && (
<group>
{/* Cabelo Superior */}
<mesh position={[0, 0.16, -0.01]} scale={[1.05, 0.7, 1.05]}>
<sphereGeometry args={[0.08, 16, 16]} />
<meshStandardMaterial color={mii.hairColor} roughness={0.8} />
</mesh>
</mesh>
)}
{avatarId === 3 && (
// Neon Ring
<group position={[0, 0.1, 0]}>
<mesh>
<sphereGeometry args={[0.075, 16, 16]} />
<meshStandardMaterial color="#27272a" roughness={0.5} />
{/* Cabelo lateral caindo */}
<mesh position={[-0.075, 0.08, -0.02]} scale={[0.4, 1.2, 0.6]}>
<sphereGeometry args={[0.04, 16, 16]} />
<meshStandardMaterial color={mii.hairColor} roughness={0.8} />
</mesh>
<mesh rotation={[0.4, 0.2, 0.8]}>
<torusGeometry args={[0.1, 0.008, 8, 32]} />
<meshBasicMaterial color={colorObj} />
<mesh position={[0.075, 0.08, -0.02]} scale={[0.4, 1.2, 0.6]}>
<sphereGeometry args={[0.04, 16, 16]} />
<meshStandardMaterial color={mii.hairColor} roughness={0.8} />
</mesh>
</group>
)}
{avatarId === 4 && (
// Retro Bot
<group position={[0, 0.1, 0]}>
<mesh>
<boxGeometry args={[0.14, 0.14, 0.12]} />
<meshStandardMaterial color="#3f3f46" roughness={0.4} />
{/* BONÉ */}
{mii.hasCap && (
<group position={[0, 0.17, 0.01]}>
{/* Copa do Boné */}
<mesh scale={[1.05, 0.65, 1.05]}>
<sphereGeometry args={[0.08, 16, 16]} />
<meshStandardMaterial color={mii.capColor} roughness={0.5} />
</mesh>
{/* Olhos */}
<mesh position={[-0.04, 0.02, 0.062]}>
{/* Aba do Boné (virada para trás) */}
<mesh position={[0, 0, -0.08]} rotation={[0.1, 0, 0]}>
<boxGeometry args={[0.11, 0.008, 0.08]} />
<meshStandardMaterial color={mii.capColor} roughness={0.5} />
</mesh>
</group>
)}
{/* CHAPÉU DE PALHA */}
{mii.hasHat && (
<group position={[0, 0.17, 0]}>
{/* Copa do Chapéu */}
<mesh>
<cylinderGeometry args={[0.055, 0.065, 0.05, 16]} />
<meshStandardMaterial color={mii.hatColor} roughness={0.8} />
</mesh>
{/* Aba do Chapéu */}
<mesh position={[0, -0.02, 0]}>
<cylinderGeometry args={[0.12, 0.12, 0.006, 16]} />
<meshStandardMaterial color={mii.hatColor} roughness={0.8} />
</mesh>
</group>
)}
{/* OLHOS */}
<mesh position={[-0.028, 0.11, 0.07]} rotation={[0.08, -0.15, 0]}>
<sphereGeometry args={[0.01, 8, 8]} />
<meshBasicMaterial color="#18181b" />
</mesh>
<mesh position={[0.028, 0.11, 0.07]} rotation={[0.08, 0.15, 0]}>
<sphereGeometry args={[0.01, 8, 8]} />
<meshBasicMaterial color="#18181b" />
</mesh>
{/* SOBRANCELHAS */}
<mesh position={[-0.028, 0.125, 0.072]} rotation={[0.05, -0.15, 0.08]}>
<boxGeometry args={[0.028, 0.005, 0.005]} />
<meshBasicMaterial color="#18181b" />
</mesh>
<mesh position={[0.028, 0.125, 0.072]} rotation={[0.05, 0.15, -0.08]}>
<boxGeometry args={[0.028, 0.005, 0.005]} />
<meshBasicMaterial color="#18181b" />
</mesh>
{/* NARIZ */}
<mesh position={[0, 0.09, 0.076]} scale={[1, 1.2, 1.2]}>
<sphereGeometry args={[0.008, 8, 8]} />
<meshStandardMaterial color={mii.skin} roughness={0.6} />
</mesh>
{/* BOCA (Sorriso) */}
<mesh position={[0, 0.065, 0.073]} rotation={[0.1, 0, 0]} scale={[1, 0.75, 1]}>
<torusGeometry args={[0.015, 0.004, 4, 16, Math.PI]} />
<meshBasicMaterial color="#b91c1c" />
</mesh>
{/* BARBA E BIGODE */}
{mii.beard && (
<group>
{/* Bigode */}
<mesh position={[0, 0.075, 0.075]}>
<boxGeometry args={[0.04, 0.006, 0.005]} />
<meshBasicMaterial color="#713f12" />
</mesh>
{/* Barbicha */}
<mesh position={[0, 0.045, 0.072]} scale={[1.2, 1, 1]}>
<sphereGeometry args={[0.015, 8, 8]} />
<meshBasicMaterial color={colorObj} />
</mesh>
<mesh position={[0.04, 0.02, 0.062]}>
<sphereGeometry args={[0.015, 8, 8]} />
<meshBasicMaterial color={colorObj} />
<meshStandardMaterial color="#713f12" roughness={0.8} />
</mesh>
</group>
)}
{avatarId === 5 && (
// Crystal Prism
<mesh position={[0, 0.1, 0]}>
<coneGeometry args={[0.08, 0.18, 5]} />
<meshStandardMaterial color="#6b21a8" roughness={0.1} metalness={0.7} transparent opacity={0.85} />
<mesh position={[0, -0.02, 0.05]} scale={[1, 0.2, 1]}>
<sphereGeometry args={[0.03]} />
<meshBasicMaterial color={colorObj} />
{/* ÓCULOS */}
{mii.hasGlasses && (
<group position={[0, 0.11, 0.072]}>
{/* Lente esquerda */}
<mesh position={[-0.028, 0, 0.002]}>
<ringGeometry args={[0.016, 0.019, 16]} />
<meshBasicMaterial color={mii.glassesColor} />
</mesh>
</mesh>
)}
{avatarId === 6 && (
// Vector Visor
<group position={[0, 0.1, 0]}>
<mesh>
<cylinderGeometry args={[0.065, 0.08, 0.14, 6]} />
<meshStandardMaterial color="#0f172a" roughness={0.2} metalness={0.9} />
{/* Lente direita */}
<mesh position={[0.028, 0, 0.002]}>
<ringGeometry args={[0.016, 0.019, 16]} />
<meshBasicMaterial color={mii.glassesColor} />
</mesh>
<mesh position={[0, 0.03, 0.05]} rotation={[0.2, 0, 0]}>
<boxGeometry args={[0.08, 0.02, 0.04]} />
<meshBasicMaterial color={colorObj} />
</mesh>
</group>
)}
{avatarId === 7 && (
// Quantum Core
<group position={[0, 0.1, 0]}>
<mesh>
<sphereGeometry args={[0.045, 16, 16]} />
<meshBasicMaterial color={colorObj} />
</mesh>
<mesh rotation={[0.5, 0, 0]}>
<torusGeometry args={[0.09, 0.005, 8, 24]} />
<meshBasicMaterial color="#ffffff" transparent opacity={0.8} />
</mesh>
<mesh rotation={[0, 0.5, 1.2]}>
<torusGeometry args={[0.075, 0.005, 8, 24]} />
<meshBasicMaterial color={colorObj} transparent opacity={0.6} />
</mesh>
</group>
)}
{avatarId === 8 && (
// Obsidian Shell
<group position={[0, 0.1, 0]}>
<mesh>
<icosahedronGeometry args={[0.09, 1]} />
<meshStandardMaterial color="#18181b" roughness={0.1} metalness={0.9} flatShading />
</mesh>
<mesh scale={[0.5, 0.5, 0.5]}>
<sphereGeometry args={[0.08]} />
<meshBasicMaterial color={colorObj} />
</mesh>
</group>
)}
{avatarId === 9 && (
// Synth Head
<group position={[0, 0.1, 0]}>
<mesh>
<sphereGeometry args={[0.08, 8, 8]} />
<meshStandardMaterial color="#09090b" roughness={0.5} flatShading />
</mesh>
<mesh position={[0, 0.015, 0.055]}>
<boxGeometry args={[0.13, 0.02, 0.01]} />
<meshBasicMaterial color={colorObj} />
</mesh>
</group>
)}
{avatarId === 10 && (
// Cosmic Helix
<group position={[0, 0.1, 0]}>
<mesh rotation={[0, 0, Date.now() * 0.002]}>
<torusKnotGeometry args={[0.065, 0.015, 32, 4]} />
<meshStandardMaterial color={colorObj} roughness={0.2} metalness={0.7} />
{/* Ponte */}
<mesh position={[0, 0, 0.002]}>
<boxGeometry args={[0.028, 0.004, 0.002]} />
<meshBasicMaterial color={mii.glassesColor} />
</mesh>
</group>
)}
@@ -219,7 +231,47 @@ function ThreeAvatar({ avatarId, color, isTalking, username, ...props }: { avata
);
}
// Mesa, Cadeiras e Detalhes da Sala de Reunião
// Carregador e Escalador Inteligente do Modelo Real (Adaptado para 1 Metro de tamanho)
function HologramModel({ model }: { model: { url: string } }) {
const { scene } = useGLTF(model.url);
const clone = useMemo(() => scene.clone(), [scene]);
const groupRef = useRef<THREE.Group>(null);
useEffect(() => {
if (!groupRef.current) return;
// Medir limites tridimensionais da maquete importada
const box = new THREE.Box3().setFromObject(clone);
const size = new THREE.Vector3();
box.getSize(size);
const maxDim = Math.max(size.x, size.y, size.z);
if (maxDim > 0) {
// Ajusta a escala de forma inteligente para ter aproximadamente 1 metro (ex: 0.95m)
const targetSize = 0.95;
const scaleFactor = targetSize / maxDim;
groupRef.current.scale.setScalar(scaleFactor);
// Centralizar o pivot em (0,0,0)
const center = new THREE.Vector3();
box.getCenter(center);
// Centraliza e levanta ligeiramente acima da mesa
clone.position.copy(center).multiplyScalar(-scaleFactor);
clone.position.y += 0.02;
}
}, [clone]);
return (
<group ref={groupRef}>
<primitive object={clone} />
</group>
);
}
// Cenário Claro, Limpo e Iluminado da Sala de Reunião
function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any }) {
const tableGeom = useMemo(() => {
// Geometria da mesa em U
@@ -244,46 +296,46 @@ function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any
return (
<group>
{/* Piso Industrial */}
{/* Piso de Laca Clara (Sala muito mais clara e iluminada!) */}
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
<planeGeometry args={[12, 8]} />
<meshStandardMaterial color="#1e293b" roughness={0.85} metalness={0.1} />
<meshStandardMaterial color="#e2e8f0" roughness={0.4} metalness={0.1} />
</mesh>
{/* Tapete Central */}
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.001, 0]}>
<planeGeometry args={[4.8, 3.2]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#94a3b8" roughness={0.7} />
</mesh>
{/* Paredes e Painéis de Fundo */}
{/* Paredes Claras */}
<mesh position={[0, 2, -4]}>
<planeGeometry args={[12, 4]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#f1f5f9" roughness={0.9} />
</mesh>
{/* Janelas panorâmicas com vista simulada (gradiente brilhante nas laterais) */}
{/* Janelas Panorâmicas Ensolaradas */}
<mesh position={[-6, 2, 0]} rotation={[0, Math.PI / 2, 0]}>
<planeGeometry args={[8, 4]} />
<meshStandardMaterial color="#1e293b" roughness={0.3} emmissive="#0284c7" />
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
</mesh>
<mesh position={[6, 2, 0]} rotation={[0, -Math.PI / 2, 0]}>
<planeGeometry args={[8, 4]} />
<meshStandardMaterial color="#1e293b" roughness={0.3} emmissive="#0284c7" />
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
</mesh>
{/* Mesa Elíptica em U */}
{/* Mesa Elíptica em U (Madeira Mel/Carvalho Clara e Acolhedora) */}
<group position={[0, 0.8, 0]} rotation={[Math.PI / 2, 0, 0]}>
<mesh geometry={tableGeom}>
<meshStandardMaterial color="#451a03" roughness={0.1} metalness={0.15} />
<meshStandardMaterial color="#d97706" roughness={0.2} metalness={0.1} />
</mesh>
</group>
{/* Pés da mesa (suporte) */}
{/* Pés da mesa (Cromados) */}
{[-1.3, 1.3].map((x) =>
[-0.7, 0.7].map((z) => (
<mesh key={`${x}-${z}`} position={[x, 0.4, z]}>
<cylinderGeometry args={[0.04, 0.04, 0.8]} />
<meshStandardMaterial color="#1e293b" metalness={0.8} roughness={0.2} />
<cylinderGeometry args={[0.035, 0.035, 0.8]} />
<meshStandardMaterial color="#d4d4d8" metalness={0.9} roughness={0.1} />
</mesh>
))
)}
@@ -292,39 +344,44 @@ function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any
<group position={[0, 0.78, 0]}>
<mesh rotation={[-Math.PI / 2, 0, 0]}>
<cylinderGeometry args={[0.26, 0.28, 0.05, 32]} />
<meshStandardMaterial color="#0f172a" metalness={0.9} roughness={0.1} />
<meshStandardMaterial color="#1e293b" metalness={0.8} roughness={0.2} />
</mesh>
{/* Placa metálica brilhante */}
{/* Anel de luz azul brilhante */}
<mesh position={[0, 0.026, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<ringGeometry args={[0, 0.22, 32]} />
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.6} />
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.7} />
</mesh>
{/* Feixe de Luz Holográfico */}
<mesh position={[0, 0.45, 0]}>
<cylinderGeometry args={[0.28, 0.2, 0.9, 32, 1, true]} />
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.12} side={THREE.DoubleSide} />
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.15} side={THREE.DoubleSide} />
</mesh>
{/* Modelo 3D da Maquete Holográfica (Representação/Pivot) */}
<group position={[0, 0.45, 0]} rotation={[0, Date.now() * 0.00015, 0]}>
{currentActiveModel ? (
{/* Modelo 3D da Maquete Holográfica */}
<group position={[0, 0.45, 0]} rotation={[0, Date.now() * 0.00012, 0]}>
<Suspense fallback={
// Carregador holográfico temporário
<mesh>
<dodecahedronGeometry args={[0.25]} />
<meshStandardMaterial color="#00f5ff" roughness={0.1} metalness={0.8} transparent opacity={0.8} emissive="#0ea5e9" emissiveIntensity={0.6} wireframe />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial color="#00f5ff" wireframe />
</mesh>
) : (
// Holograma Padrão (Estrutura geométrica representativa de maquete)
<group>
<mesh position={[0, 0, 0]}>
<octahedronGeometry args={[0.22]} />
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
</mesh>
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
<boxGeometry args={[0.25, 0.04, 0.25]} />
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
</mesh>
</group>
)}
}>
{currentActiveModel ? (
<HologramModel model={currentActiveModel} />
) : (
// Holograma Padrão Geométrico se não houver maquete carregada
<group>
<mesh position={[0, 0, 0]}>
<octahedronGeometry args={[0.22]} />
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
</mesh>
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
<boxGeometry args={[0.25, 0.04, 0.25]} />
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
</mesh>
</group>
)}
</Suspense>
</group>
</group>
@@ -334,31 +391,31 @@ function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any
{/* Base giratória metálica */}
<mesh position={[0, 0.25, 0]}>
<cylinderGeometry args={[0.015, 0.015, 0.5]} />
<meshStandardMaterial color="#71717a" metalness={0.9} roughness={0.1} />
<meshStandardMaterial color="#a1a1aa" metalness={0.9} roughness={0.1} />
</mesh>
{/* Pés base */}
<mesh position={[0, 0.02, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<ringGeometry args={[0, 0.22, 4]} />
<meshStandardMaterial color="#3f3f46" metalness={0.85} roughness={0.2} />
<meshStandardMaterial color="#71717a" metalness={0.85} roughness={0.2} />
</mesh>
{/* Assento */}
<mesh position={[0, 0.52, 0]}>
<boxGeometry args={[0.28, 0.05, 0.26]} />
<meshStandardMaterial color="#1e293b" roughness={0.7} />
<meshStandardMaterial color="#334155" roughness={0.7} />
</mesh>
{/* Encosto */}
<mesh position={[0, 0.78, -0.11]} rotation={[0.08, 0, 0]}>
<boxGeometry args={[0.26, 0.44, 0.04]} />
<meshStandardMaterial color="#1e293b" roughness={0.7} />
<meshStandardMaterial color="#334155" roughness={0.7} />
</mesh>
</group>
))}
{/* Iluminação da Sala */}
<ambientLight intensity={0.4} />
<directionalLight position={[5, 8, 5]} intensity={1.0} castShadow />
<directionalLight position={[-5, 5, -5]} intensity={0.3} />
<pointLight position={[0, 3.5, 0]} intensity={1.5} color="#0ea5e9" distance={6} />
{/* Iluminação Super Clara e Acolhedora da Sala */}
<hemisphereLight skyColor="#ffffff" groundColor="#64748b" intensity={1.4} />
<directionalLight position={[6, 12, 6]} intensity={1.8} castShadow />
<directionalLight position={[-6, 8, -6]} intensity={0.5} />
<pointLight position={[0, 3.5, 0]} intensity={2.0} color="#0ea5e9" distance={8} />
</group>
);
}
@@ -543,22 +600,22 @@ export default function MeetingRoom() {
Lobby de <span className="text-primary">Reunião Virtual</span>
</h2>
<p className="mt-1.5 text-xs font-mono uppercase tracking-wider text-muted-foreground">
Prepare seu avatar e escolha seu assento
Escolha seu visual Mii e selecione seu assento
</p>
</div>
<div className="grid grid-cols-1 gap-8 md:grid-cols-12">
{/* Seletor de Avatar (Lado Esquerdo - 5 colunas) */}
<div className="flex flex-col items-center justify-center rounded-xl border bg-muted/30 p-6 md:col-span-5 relative">
<span className="absolute top-3 left-4 font-mono text-[9px] uppercase tracking-widest text-muted-foreground">Escolha o Avatar</span>
<span className="absolute top-3 left-4 font-mono text-[9px] uppercase tracking-widest text-muted-foreground">Modelo de Avatar</span>
{/* Modelo Virtual do Avatar no Centro */}
<div className="h-44 w-full flex items-center justify-center relative bg-slate-950/40 rounded-lg overflow-hidden border border-border">
<Canvas camera={{ position: [0, 0.15, 0.35], fov: 50 }}>
<ambientLight intensity={0.5} />
{/* Modelo Virtual do Mii no Centro */}
<div className="h-44 w-full flex items-center justify-center relative bg-slate-900/60 rounded-lg overflow-hidden border border-border">
<Canvas camera={{ position: [0, 0.16, 0.28], fov: 40 }}>
<ambientLight intensity={0.8} />
<directionalLight position={[2, 3, 2]} intensity={1.5} />
<pointLight position={[0, 0.15, 0.2]} intensity={1} color={currentAvatar.color} />
<ThreeAvatar avatarId={selectedAvatar} color={currentAvatar.color} username={name || 'Avatar'} position={[0, 0, 0]} isTalking={true} />
<MiiAvatar avatarId={selectedAvatar} username={name || 'Mii'} position={[0, 0, 0]} isTalking={true} />
</Canvas>
{/* Navegadores de carrossel de avatar */}
@@ -572,7 +629,7 @@ export default function MeetingRoom() {
{/* Informações do avatar */}
<div className="mt-4 text-center">
<span className="font-mono text-xs font-bold uppercase tracking-wider" style={{ color: currentAvatar.color }}>
<span className="font-mono text-xs font-bold uppercase tracking-wider text-foreground">
{currentAvatar.name}
</span>
<p className="text-[10px] text-muted-foreground mt-0.5">{currentAvatar.desc}</p>
@@ -668,10 +725,10 @@ export default function MeetingRoom() {
// --- Renderização da Sala de Reunião Ativa ---
return (
<div className="flex h-screen overflow-hidden bg-slate-950 text-white relative">
{/* 3D Canvas - Sala Virtual */}
<div className="flex h-screen overflow-hidden bg-slate-950 text-white relative animate-in fade-in duration-300">
{/* 3D Canvas - Sala Virtual Clara */}
<div className="flex-1 h-full relative">
<Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows>
<Canvas camera={{ position: [0, 2.5, 2.8], fof: 45 }} shadows>
<ThreeMeetingRoomScene currentActiveModel={activeModel} />
{/* Renderizar avatares dos outros participantes na sala */}
@@ -683,16 +740,13 @@ export default function MeetingRoom() {
// Não renderiza a nós mesmos na cena como avatar, nossa câmera já está na cadeira!
if (peer.chairId === selectedChair) return null;
const avatarPreset = AVATAR_PRESETS[peer.avatarId - 1] || AVATAR_PRESETS[0];
return (
<ThreeAvatar
<MiiAvatar
key={peer.presence_ref}
avatarId={peer.avatarId}
color={avatarPreset.color}
username={peer.username}
position={chairConf.pos}
isTalking={isTalking && Math.random() > 0.5} // Simulação simples de fala para os peers
isTalking={isTalking && Math.random() > 0.5} // Simulação de fala
/>
);
})}
@@ -715,7 +769,7 @@ export default function MeetingRoom() {
<div className="flex flex-col">
<span className="font-mono text-[9px] text-muted-foreground uppercase">Reunião Conectada</span>
<span className="font-mono text-xs font-semibold text-foreground">
Cadeiras Ocupadas: {activeUsersCount}/8
Membros Online: {activeUsersCount}/8
</span>
</div>
</div>
@@ -759,9 +813,9 @@ export default function MeetingRoom() {
</Button>
<div className="pointer-events-auto rounded-xl border bg-slate-900/90 backdrop-blur-md px-4 py-2 flex flex-col justify-center shadow-2xl">
<span className="font-mono text-[8px] text-muted-foreground uppercase">Dica Imersiva</span>
<span className="font-mono text-[8px] text-muted-foreground uppercase">Dica de Visão</span>
<span className="text-[10px] text-foreground font-mono">
Clique e arraste com o botão esquerdo do mouse para girar a maquete e a sala
Clique e arraste com o botão esquerdo para girar a mesa e olhar os avatares
</span>
</div>
</div>
@@ -773,9 +827,9 @@ export default function MeetingRoom() {
<div className="border-b border-slate-800 p-4 flex items-center justify-between">
<div className="flex items-center gap-2">
<MessageSquare className="h-4.5 w-4.5 text-primary" />
<span className="font-mono text-xs font-bold uppercase tracking-wider">Chat & Notas</span>
<span className="font-mono text-xs font-bold uppercase tracking-wider">Chat da Reunião</span>
</div>
<span className="font-mono text-[10px] bg-primary/20 text-primary px-1.5 py-0.5 rounded">Realtime</span>
<span className="font-mono text-[10px] bg-primary/20 text-primary px-1.5 py-0.5 rounded">Tempo Real</span>
</div>
{/* Histórico do Chat */}