/ concept-collection / mesh-converter
Sign in
concept-collection / mesh-converter
mesh-converter / src / MeshView.tsx
116 lines · 3.7 KBCodeBlameHistory
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 1import { useEffect, useMemo, useState } from 'react'
edd4e73Mesh converter: upload, view, and export meshes across formatsJeremy Magland 2import { Canvas } from '@react-three/fiber'
3import { OrbitControls } from '@react-three/drei'
4import * as THREE from 'three'
5import type { MeshData } from './mesh/types'
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 7type ViewMode = 'shaded' | 'wire' | 'both' | 'points'
9const VIEW_MODES: { id: ViewMode; label: string }[] = [
10 { id: 'shaded', label: 'Shaded' },
11 { id: 'wire', label: 'Wire' },
12 { id: 'both', label: 'Both' },
13 { id: 'points', label: 'Points' },
16const PLAIN_COLOR = '#8fb4d9'
18function MeshObject({ mesh, mode }: { mesh: MeshData; mode: ViewMode }) {
edd4e73Mesh converter: upload, view, and export meshes across formatsJeremy Magland 19 const geometry = useMemo(() => {
20 const g = new THREE.BufferGeometry()
21 g.setAttribute('position', new THREE.Float32BufferAttribute(mesh.positions, 3))
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 22 g.setIndex(new THREE.Uint32BufferAttribute(mesh.indices, 1))
24 g.setAttribute('normal', new THREE.Float32BufferAttribute(mesh.normals, 3))
25 } else {
26 g.computeVertexNormals()
27 }
28 if (mesh.colors) {
29 g.setAttribute('color', new THREE.Float32BufferAttribute(mesh.colors, 3))
30 }
31 // Center and scale to a consistent size so the fixed camera always frames it
32 g.center()
33 g.computeBoundingSphere()
34 return g
35 }, [mesh])
37 useEffect(() => () => geometry.dispose(), [geometry])
39 const scale = 1.6 / (geometry.boundingSphere?.radius || 1)
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 40 const useVertexColors = !!mesh.colors
41 // material settings are baked into the compiled shader; remount materials
42 // when they change so three.js rebuilds the program
43 const matKey = `${mode}-${useVertexColors ? 'vc' : 'plain'}`
45 return (
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 46 <group scale={scale}>
47 {(mode === 'shaded' || mode === 'both') && (
48 <mesh geometry={geometry}>
49 <meshStandardMaterial
50 key={matKey}
51 vertexColors={useVertexColors}
52 color={useVertexColors ? 'white' : PLAIN_COLOR}
53 roughness={0.55}
54 metalness={0.1}
55 side={THREE.DoubleSide}
56 polygonOffset={mode === 'both'}
57 polygonOffsetFactor={1}
58 polygonOffsetUnits={1}
59 />
60 </mesh>
61 )}
62 {(mode === 'wire' || mode === 'both') && (
63 <mesh geometry={geometry}>
64 <meshBasicMaterial
65 key={matKey}
66 wireframe
67 // over the shaded surface use thin dark lines; standalone
68 // wireframe keeps the mesh's own coloring
69 vertexColors={mode === 'wire' && useVertexColors}
70 color={mode === 'both' ? '#10161f' : useVertexColors ? 'white' : PLAIN_COLOR}
71 transparent={mode === 'both'}
72 opacity={mode === 'both' ? 0.35 : 1}
73 />
74 </mesh>
75 )}
76 {mode === 'points' && (
77 <points geometry={geometry}>
78 <pointsMaterial
79 key={matKey}
80 vertexColors={useVertexColors}
81 color={useVertexColors ? 'white' : PLAIN_COLOR}
82 size={0.02}
83 />
84 </points>
85 )}
86 </group>
90export function MeshView({ mesh }: { mesh: MeshData }) {
50876cdConvert real mesh formats with meshio via PyodideJeremy Magland 91 const [mode, setMode] = useState<ViewMode>('both')
95 <div className="view-toolbar">
96 {VIEW_MODES.map((m) => (
97 <button
98 key={m.id}
99 className={mode === m.id ? 'active' : ''}
100 onClick={() => setMode(m.id)}
101 >
102 {m.label}
103 </button>
104 ))}
105 </div>
106 <Canvas camera={{ position: [2.6, 1.8, 2.6], fov: 45 }}>
107 <color attach="background" args={['#16181d']} />
108 <ambientLight intensity={0.5} />
109 <directionalLight position={[5, 8, 4]} intensity={1.6} />
110 <directionalLight position={[-4, -3, -6]} intensity={0.4} />
111 <MeshObject mesh={mesh} mode={mode} />
112 <OrbitControls makeDefault />
113 </Canvas>
114 </>
moveopenescclose