import React, { useState, Suspense, useRef, useMemo } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { OrbitControls, Stage, PerspectiveCamera, Environment, ContactShadows, useGLTF, } from '@react-three/drei'; import { TeapotGeometry } from 'three/examples/jsm/geometries/TeapotGeometry'; import { Link } from 'react-router-dom'; import { ArrowLeftIcon, DownloadSimpleIcon, ArrowsClockwiseIcon, UploadIcon, } from '@phosphor-icons/react'; import Seo from '../../components/Seo'; import { useToast } from '../../hooks/useToast'; import BreadcrumbTitle from '../../components/BreadcrumbTitle'; import CustomSlider from '../../components/CustomSlider'; import CustomColorPicker from '../../components/CustomColorPicker'; const DefaultModel = ({ color, wireframe, speed }) => { const meshRef = useRef(); useFrame((state, delta) => { if (meshRef.current) { meshRef.current.rotation.y += delta * speed; } }); const geometry = useMemo(() => new TeapotGeometry(1, 15), []); return ( ); }; const CustomModel = ({ url, color, wireframe, speed }) => { const { scene } = useGLTF(url); const meshRef = useRef(); useFrame((state, delta) => { if (meshRef.current) { meshRef.current.rotation.y += delta * speed; } }); // Apply properties to the model scene.traverse((child) => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; if (child.material) { child.material.wireframe = wireframe; // Only apply color if it's a simple model, or maybe just tint it // For now, let's keep original materials but allow wireframe toggle } } }); return ; }; const ModelViewerPage = () => { const { addToast } = useToast(); // App State const [modelUrl, setModelUrl] = useState(null); const [color, setColor] = useState('#10b981'); const [bgColor, setBgColor] = useState('#080808'); const [wireframe, setWireframe] = useState(false); const [rotationSpeed, setRotationSpeed] = useState(0.5); const [intensity, setIntensity] = useState(1); const handleModelUpload = (e) => { const file = e.target.files[0]; if (file) { const url = URL.createObjectURL(file); setModelUrl(url); addToast({ title: 'Model Loaded', message: `${file.name} is ready for inspection.`, duration: 3000, }); } }; const handleDownloadSnapshot = () => { const canvas = document.querySelector('canvas'); if (canvas) { const link = document.createElement('a'); link.download = 'model-snapshot.png'; link.href = canvas.toDataURL('image/png'); link.click(); addToast({ title: 'Snapshot Saved', message: 'The 3D view has been captured.', duration: 3000, }); } }; const randomize = () => { const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16); const randomBg = '#' + Math.floor(Math.random() * 2105376).toString(16); // Prefer dark colors setColor(randomColor); setBgColor(randomBg); setRotationSpeed(Math.random() * 2); setIntensity(0.5 + Math.random() * 1.5); }; return (
{/* Header */} Back to Terminal
{/* Controls Panel - Now at top in a grid */}

Source & Actions

{modelUrl && ( )}

Appearance

Dynamics & Stage

{/* Viewer Area - Now full width and shorter */}
Initializing 3D Buffer...
} > {modelUrl ? ( ) : ( )} {/* UI Overlays */}
Axis_Locked: False Viewport: Dynamic
Triangles 12,288
Vertices 6,146
); }; export default ModelViewerPage;