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 */}
Appearance
{/* Viewer Area - Now full width and shorter */}
Initializing 3D Buffer...
}
>
{/* UI Overlays */}
Axis_Locked: False
Viewport: Dynamic
Triangles
12,288
Vertices
6,146
);
};
export default ModelViewerPage;