diff --git a/src/pages/MeetingRoom.tsx b/src/pages/MeetingRoom.tsx index 922dded..19c3a51 100644 --- a/src/pages/MeetingRoom.tsx +++ b/src/pages/MeetingRoom.tsx @@ -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(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 ( - {/* Nome do Participante acima da cabeça */} + {/* Balão com Nome acima da cabeça */} - {/* Painel do nome */} - + {username} @@ -76,142 +94,136 @@ function ThreeAvatar({ avatarId, color, isTalking, username, ...props }: { avata )} - {/* Renderizadores dos avatares baseados em presets geométricos */} - {avatarId === 1 && ( - // Cyber Helmet - - - - - - - + {/* CABEÇA MII (Esfera ovalada de pele) */} + + + + + + {/* CABELO */} + {mii.hairStyle === 'short' && ( + + + )} - {avatarId === 2 && ( - // Gold Mask - - - - - - + {mii.hairStyle === 'long' && ( + + {/* Cabelo Superior */} + + + - - )} - - {avatarId === 3 && ( - // Neon Ring - - - - + {/* Cabelo lateral caindo */} + + + - - - + + + )} - {avatarId === 4 && ( - // Retro Bot - - - - + {/* BONÉ */} + {mii.hasCap && ( + + {/* Copa do Boné */} + + + - {/* Olhos */} - + {/* Aba do Boné (virada para trás) */} + + + + + + )} + + {/* CHAPÉU DE PALHA */} + {mii.hasHat && ( + + {/* Copa do Chapéu */} + + + + + {/* Aba do Chapéu */} + + + + + + )} + + {/* OLHOS */} + + + + + + + + + + {/* SOBRANCELHAS */} + + + + + + + + + + {/* NARIZ */} + + + + + + {/* BOCA (Sorriso) */} + + + + + + {/* BARBA E BIGODE */} + {mii.beard && ( + + {/* Bigode */} + + + + + {/* Barbicha */} + - - - - - + )} - {avatarId === 5 && ( - // Crystal Prism - - - - - - + {/* ÓCULOS */} + {mii.hasGlasses && ( + + {/* Lente esquerda */} + + + - - )} - - {avatarId === 6 && ( - // Vector Visor - - - - + {/* Lente direita */} + + + - - - - - - )} - - {avatarId === 7 && ( - // Quantum Core - - - - - - - - - - - - - - - )} - - {avatarId === 8 && ( - // Obsidian Shell - - - - - - - - - - - )} - - {avatarId === 9 && ( - // Synth Head - - - - - - - - - - - )} - - {avatarId === 10 && ( - // Cosmic Helix - - - - + {/* Ponte */} + + + )} @@ -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(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 ( + + + + ); +} + +// 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 ( - {/* Piso Industrial */} + {/* Piso de Laca Clara (Sala muito mais clara e iluminada!) */} - + {/* Tapete Central */} - + - {/* Paredes e Painéis de Fundo */} + {/* Paredes Claras */} - + - {/* Janelas panorâmicas com vista simulada (gradiente brilhante nas laterais) */} + {/* Janelas Panorâmicas Ensolaradas */} - + - + - {/* Mesa Elíptica em U */} + {/* Mesa Elíptica em U (Madeira Mel/Carvalho Clara e Acolhedora) */} - + - {/* Pés da mesa (suporte) */} + {/* Pés da mesa (Cromados) */} {[-1.3, 1.3].map((x) => [-0.7, 0.7].map((z) => ( - - + + )) )} @@ -292,39 +344,44 @@ function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any - + - {/* Placa metálica brilhante */} + {/* Anel de luz azul brilhante */} - + {/* Feixe de Luz Holográfico */} - + - {/* Modelo 3D da Maquete Holográfica (Representação/Pivot) */} - - {currentActiveModel ? ( + {/* Modelo 3D da Maquete Holográfica */} + + - - + + - ) : ( - // Holograma Padrão (Estrutura geométrica representativa de maquete) - - - - - - - - - - - )} + }> + {currentActiveModel ? ( + + ) : ( + // Holograma Padrão Geométrico se não houver maquete carregada + + + + + + + + + + + )} + @@ -334,31 +391,31 @@ function ThreeMeetingRoomScene({ currentActiveModel }: { currentActiveModel: any {/* Base giratória metálica */} - + {/* Pés base */} - + {/* Assento */} - + {/* Encosto */} - + ))} - {/* Iluminação da Sala */} - - - - + {/* Iluminação Super Clara e Acolhedora da Sala */} + + + + ); } @@ -543,22 +600,22 @@ export default function MeetingRoom() { Lobby de Reunião Virtual

- Prepare seu avatar e escolha seu assento + Escolha seu visual Mii e selecione seu assento

{/* Seletor de Avatar (Lado Esquerdo - 5 colunas) */}
- Escolha o Avatar + Modelo de Avatar - {/* Modelo Virtual do Avatar no Centro */} -
- - + {/* Modelo Virtual do Mii no Centro */} +
+ + - + {/* Navegadores de carrossel de avatar */} @@ -572,7 +629,7 @@ export default function MeetingRoom() { {/* Informações do avatar */}
- + {currentAvatar.name}

{currentAvatar.desc}

@@ -668,10 +725,10 @@ export default function MeetingRoom() { // --- Renderização da Sala de Reunião Ativa --- return ( -
- {/* 3D Canvas - Sala Virtual */} +
+ {/* 3D Canvas - Sala Virtual Clara */}
- + {/* 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 ( - 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() {
Reunião Conectada - Cadeiras Ocupadas: {activeUsersCount}/8 + Membros Online: {activeUsersCount}/8
@@ -759,9 +813,9 @@ export default function MeetingRoom() {
- Dica Imersiva + Dica de Visão - 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
@@ -773,9 +827,9 @@ export default function MeetingRoom() {
- Chat & Notas + Chat da Reunião
- Realtime + Tempo Real
{/* Histórico do Chat */}