import { useEffect, useMemo, useState } from 'react' import { Canvas } from '@react-three/fiber' import { OrbitControls } from '@react-three/drei' import * as THREE from 'three' import type { MeshData } from './mesh/types' type ViewMode = 'shaded' | 'wire' | 'both' | 'points' const VIEW_MODES: { id: ViewMode; label: string }[] = [ { id: 'shaded', label: 'Shaded' }, { id: 'wire', label: 'Wire' }, { id: 'both', label: 'Both' }, { id: 'points', label: 'Points' }, ] const PLAIN_COLOR = '#8fb4d9' function MeshObject({ mesh, mode }: { mesh: MeshData; mode: ViewMode }) { const geometry = useMemo(() => { const g = new THREE.BufferGeometry() g.setAttribute('position', new THREE.Float32BufferAttribute(mesh.positions, 3)) g.setIndex(new THREE.Uint32BufferAttribute(mesh.indices, 1)) if (mesh.normals) { g.setAttribute('normal', new THREE.Float32BufferAttribute(mesh.normals, 3)) } else { g.computeVertexNormals() } if (mesh.colors) { g.setAttribute('color', new THREE.Float32BufferAttribute(mesh.colors, 3)) } // Center and scale to a consistent size so the fixed camera always frames it g.center() g.computeBoundingSphere() return g }, [mesh]) useEffect(() => () => geometry.dispose(), [geometry]) const scale = 1.6 / (geometry.boundingSphere?.radius || 1) const useVertexColors = !!mesh.colors // material settings are baked into the compiled shader; remount materials // when they change so three.js rebuilds the program const matKey = `${mode}-${useVertexColors ? 'vc' : 'plain'}` return ( {(mode === 'shaded' || mode === 'both') && ( )} {(mode === 'wire' || mode === 'both') && ( )} {mode === 'points' && ( )} ) } export function MeshView({ mesh }: { mesh: MeshData }) { const [mode, setMode] = useState('both') return ( <> {VIEW_MODES.map((m) => ( setMode(m.id)} > {m.label} ))} > ) }