Add compression ratios and animation support to 3D viewer

This commit is contained in:
Agent Zero
2026-02-15 16:53:08 +00:00
parent 22912a4235
commit eda2667201
5 changed files with 275 additions and 241 deletions

File diff suppressed because one or more lines are too long

1
dist/assets/index-0Vn4RLxh.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/index.html vendored
View File

@@ -9,7 +9,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script type="module" crossorigin src="./assets/index-2z9TbcuZ.js"></script>
<script type="module" crossorigin src="./assets/index-0Vn4RLxh.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DplxtjX9.css">
</head>
<body>

View File

@@ -1,6 +1,6 @@
import { Suspense, useState } from 'react';
import { Suspense, useState, useEffect } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls, useGLTF, Environment, ContactShadows, Center } from '@react-three/drei';
import { OrbitControls, useGLTF, useAnimations, Environment, ContactShadows, Center } from '@react-three/drei';
import { motion, AnimatePresence } from 'framer-motion';
import { Box, AlertCircle, RotateCw, ZoomIn, Move, X } from 'lucide-react';
@@ -37,12 +37,21 @@ function ViewerError({ message, onClose }: { message: string; onClose: () => voi
}
// The actual 3D model component
function Model({ url }: { url: string }) {
const { scene } = useGLTF(url);
function Model({ url, animated = false }: { url: string; animated?: boolean }) {
const { scene, animations } = useGLTF(url);
const { actions } = useAnimations(animations, scene);
// Auto-play animation if available and model is flagged as animated
useEffect(() => {
if (animated && actions && Object.keys(actions).length > 0) {
const firstAction = Object.values(actions)[0];
firstAction?.play();
}
}, [animated, actions]);
return (
<Center>
<primitive object={scene} scale={1.5} />
<primitive object={scene} scale={animated ? 1.2 : 1.5} />
</Center>
);
}
@@ -72,12 +81,12 @@ function Loader() {
// Available models with correct paths
const availableModels = [
{ name: 'T-Pose Character', file: 'models/T-pose_web.glb', size: '428 KB' },
{ name: 'Jogging Character', file: 'models/Jogging_web.glb', size: '235 KB' },
{ name: 'Old Man Idle', file: 'models/Old Man Idle_web.glb', size: '282 KB' },
{ name: 'Doless Model', file: 'models/Doless_web.glb', size: '65 KB' },
{ name: 'AI Character', file: 'models/ComfyUI_00003__web.glb', size: '752 KB' },
{ name: 'David Bust (OBJ+MTL)', file: 'models/bust_of_David_web.glb', size: '6 MB' },
{ name: 'T-Pose Character', file: 'models/T-pose_web.glb', size: '418 KB', original: '2.1 MB', compression: '80%' },
{ name: 'Jogging Character', file: 'models/Jogging_web.glb', size: '235 KB', original: '1.8 MB FBX', compression: '87%', animated: true },
{ name: 'Old Man Idle', file: 'models/Old Man Idle_web.glb', size: '282 KB', original: '2.1 MB FBX', compression: '87%', animated: true },
{ name: 'Doless Model', file: 'models/Doless_web.glb', size: '66 KB', original: '6.6 MB OBJ', compression: '99%' },
{ name: 'AI Character', file: 'models/ComfyUI_00003__web.glb', size: '753 KB', original: '4.9 MB', compression: '85%' },
{ name: 'David Bust (OBJ+MTL)', file: 'models/bust_of_David_web.glb', size: '6 MB', original: '29 MB OBJ', compression: '79%' },
];
export default function Model3DViewer() {
@@ -133,8 +142,33 @@ export default function Model3DViewer() {
</div>
<h3>{model.name}</h3>
<p style={{ color: 'var(--text-muted)', fontSize: '0.9rem' }}>
Web-optimized GLB {model.size}
{model.original} {model.size}
</p>
{model.animated && (
<span style={{
background: 'rgba(6, 182, 212, 0.2)',
color: '#06b6d4',
padding: '0.25rem 0.5rem',
borderRadius: '4px',
fontSize: '0.75rem',
fontWeight: 600,
marginTop: '0.5rem',
display: 'inline-block'
}}>
Animated
</span>
)}
<div style={{
marginTop: '0.75rem',
padding: '0.4rem 0.75rem',
background: 'rgba(139, 92, 246, 0.1)',
borderRadius: '6px',
display: 'inline-block'
}}>
<span style={{ color: '#8b5cf6', fontSize: '0.8rem', fontWeight: 600 }}>
Draco: -{model.compression} smaller
</span>
</div>
<div style={{
marginTop: '1rem',
padding: '0.5rem 1rem',
@@ -212,7 +246,7 @@ export default function Model3DViewer() {
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} intensity={1} />
<Environment preset="city" />
<Model url={selectedModel} />
<Model url={selectedModel} animated={availableModels.find(m => m.file === selectedModel)?.animated} />
<ContactShadows
position={[0, -1.5, 0]}
opacity={0.4}