🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 12:29:47

This commit is contained in:
2026-05-25 12:29:47 +00:00
parent f876e868d6
commit e8f4e2b18a
+97 -38
View File
@@ -77,6 +77,28 @@ interface PeerData {
presence_ref: string; presence_ref: string;
} }
// Detector nativo de suporte ao WebGL
function isWebGLAvailable() {
try {
const canvas = document.createElement('canvas');
return !!(
window.WebGLRenderingContext &&
(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
);
} catch (e) {
return false;
}
}
// Hook de verificação de suporte a WebGL no cliente
function useWebGLSupport() {
const [supported, setSupported] = useState(true);
useEffect(() => {
setSupported(isWebGLAvailable());
}, []);
return supported;
}
// --- Configurações da Mesa de Reunião --- // --- Configurações da Mesa de Reunião ---
const CHAIRS = [ const CHAIRS = [
{ id: '1', name: 'Assento 1', pos: [-1.2, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] }, { id: '1', name: 'Assento 1', pos: [-1.2, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] },
@@ -930,6 +952,7 @@ function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusM
// --- Componente Principal da Tela --- // --- Componente Principal da Tela ---
export default function MeetingRoom() { export default function MeetingRoom() {
const isWebGLSupported = useWebGLSupport();
const { roomId } = useParams<{ roomId?: string }>(); const { roomId } = useParams<{ roomId?: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const models = useModelStore((s) => s.models); const models = useModelStore((s) => s.models);
@@ -1352,12 +1375,22 @@ export default function MeetingRoom() {
{/* Enquadramento centralizado perfeito (Camera apontando e fov adequado) */} {/* Enquadramento centralizado perfeito (Camera apontando e fov adequado) */}
<div className="h-40 w-full flex items-center justify-center relative bg-slate-900/60 rounded-lg overflow-hidden border border-border"> <div className="h-40 w-full flex items-center justify-center relative bg-slate-900/60 rounded-lg overflow-hidden border border-border">
<Canvas camera={{ position: [0, 0.11, 0.28], fov: 42 }}> {isWebGLSupported ? (
<ambientLight intensity={0.8} /> <Canvas camera={{ position: [0, 0.11, 0.28], fov: 42 }}>
<directionalLight position={[2, 3, 2]} intensity={1.5} /> <ambientLight intensity={0.8} />
<pointLight position={[0, 0.15, 0.2]} intensity={1} color={currentAvatar.color} /> <directionalLight position={[2, 3, 2]} intensity={1.5} />
<MiiAvatar avatarId={selectedAvatar} username={name || 'Você'} position={[0, -0.04, 0]} isTalking={true} scale={0.6} /> <pointLight position={[0, 0.15, 0.2]} intensity={1} color={currentAvatar.color} />
</Canvas> <MiiAvatar avatarId={selectedAvatar} username={name || 'Você'} position={[0, -0.04, 0]} isTalking={true} scale={0.6} />
</Canvas>
) : (
<div className="flex flex-col items-center justify-center h-full w-full bg-slate-950/80 p-4 text-center">
<ShieldAlert className="h-7 w-7 text-amber-500 animate-pulse mb-1" />
<span className="font-mono text-[9px] font-bold text-amber-500 uppercase tracking-wider">WebGL Desativado</span>
<span className="text-[8px] text-slate-400 mt-1 max-w-[180px] leading-relaxed">
Aceleração por hardware inativa. O avatar 3D não pôde ser pré-visualizado.
</span>
</div>
)}
{/* Aviso se o avatar estiver em uso na sala */} {/* Aviso se o avatar estiver em uso na sala */}
{isAvatarInUse && ( {isAvatarInUse && (
@@ -1514,41 +1547,67 @@ export default function MeetingRoom() {
<div className="flex h-screen overflow-hidden bg-slate-950 text-white relative animate-in fade-in duration-350"> <div className="flex h-screen overflow-hidden bg-slate-950 text-white relative animate-in fade-in duration-350">
{/* 3D Canvas - Sala Clara */} {/* 3D Canvas - Sala Clara */}
<div className="flex-1 h-full relative"> <div className="flex-1 h-full relative">
<Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows gl={{ localClippingEnabled: true }}> {isWebGLSupported ? (
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} /> <Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows gl={{ localClippingEnabled: true }}>
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} />
{/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
{!isFocusMode && Object.keys(peers).map((chairKey) => {
const peer = peers[chairKey];
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
if (!chairConf) return null;
const isLocalUser = peer.chairId === selectedChair; {/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
{!isFocusMode && Object.keys(peers).map((chairKey) => {
const peer = peers[chairKey];
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
if (!chairConf) return null;
const isLocalUser = peer.chairId === selectedChair;
return ( return (
<MiiAvatar <MiiAvatar
key={peer.presence_ref} key={peer.presence_ref}
avatarId={peer.avatarId} avatarId={peer.avatarId}
username={isLocalUser ? `${peer.username} (Você)` : peer.username} username={isLocalUser ? `${peer.username} (Você)` : peer.username}
position={chairConf.pos} position={chairConf.pos}
rotation={chairConf.rot} rotation={chairConf.rot}
isTalking={isLocalUser ? isTalking : (isTalking && Math.random() > 0.5)} isTalking={isLocalUser ? isTalking : (isTalking && Math.random() > 0.5)}
isLastChatSender={peer.username === lastChatSender} isLastChatSender={peer.username === lastChatSender}
scale={1.2} scale={1.2}
/> />
); );
})} })}
<OrbitControls <OrbitControls
makeDefault makeDefault
enableDamping enableDamping
dampingFactor={0.05} dampingFactor={0.05}
minDistance={0.5} minDistance={0.5}
maxDistance={8} maxDistance={8}
maxPolarAngle={Math.PI / 2 - 0.05} maxPolarAngle={Math.PI / 2 - 0.05}
target={isFocusMode ? [0, 0, 0] : [0, 0.95, 0]} target={isFocusMode ? [0, 0, 0] : [0, 0.95, 0]}
/> />
</Canvas> </Canvas>
) : (
<div className="flex-1 h-full flex flex-col items-center justify-center p-8 bg-slate-900 border border-slate-800 rounded-xl m-4 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 opacity-95 z-0" />
<div className="relative z-10 max-w-md text-center space-y-4">
<div className="mx-auto w-16 h-16 rounded-full bg-amber-500/10 border border-amber-500/30 flex items-center justify-center animate-pulse">
<ShieldAlert className="h-8 w-8 text-amber-500" />
</div>
<div className="space-y-2">
<h3 className="text-xl font-bold tracking-tight text-slate-100 font-mono uppercase">WebGL Requerido</h3>
<p className="text-xs text-slate-400 leading-relaxed">
Aceleração por hardware (WebGL) está desativada ou não é suportada por seu navegador/dispositivo atual.
</p>
</div>
<div className="bg-slate-950/80 rounded-lg p-4 text-left border border-slate-800 text-[11px] font-mono text-slate-400 space-y-2">
<span className="text-amber-500 font-bold uppercase block text-[10px] tracking-wider mb-1">Como resolver no Chrome:</span>
<p>1. em <code className="text-primary">chrome://settings/system</code></p>
<p>2. Ative <code className="text-primary">"Usar aceleração de hardware quando disponível"</code></p>
<p>3. Reinicie o navegador e recarregue esta página.</p>
</div>
<div className="text-[10px] text-muted-foreground italic">
* Nota: O chat lateral, lista de participantes e chamadas continuam funcionando normalmente.
</div>
</div>
</div>
)}
{/* Painel do Apresentador (Controles de Manipulação Sincronizados) */} {/* Painel do Apresentador (Controles de Manipulação Sincronizados) */}
{isPresenter && ( {isPresenter && (