import * as THREE from 'three'; import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'; type SupportedExt = 'glb' | 'obj' | 'stl'; export function getSupportedExtension(fileName: string): SupportedExt | null { const ext = fileName.split('.').pop()?.toLowerCase(); if (ext === 'glb' || ext === 'obj' || ext === 'stl') return ext; return null; } export const ACCEPTED_EXTENSIONS = '.glb,.obj,.stl'; /** * Convert an OBJ or STL file (ArrayBuffer) into a GLB Blob. * GLB files are returned as-is from their original blob. */ export async function convertToGLB( buffer: ArrayBuffer, ext: 'obj' | 'stl', fileName: string ): Promise<{ blob: Blob; fileName: string; fileSize: number }> { const scene = new THREE.Scene(); const material = new THREE.MeshStandardMaterial({ color: 0x8899aa, metalness: 0.85, roughness: 0.35, }); if (ext === 'stl') { const loader = new STLLoader(); const geometry = loader.parse(buffer); geometry.computeVertexNormals(); const mesh = new THREE.Mesh(geometry, material); mesh.name = fileName.replace(/\.stl$/i, ''); scene.add(mesh); } else { const loader = new OBJLoader(); const text = new TextDecoder().decode(buffer); const obj = loader.parse(text); obj.traverse((child) => { if (child instanceof THREE.Mesh) { child.material = material; } }); obj.name = fileName.replace(/\.obj$/i, ''); scene.add(obj); } const exporter = new GLTFExporter(); const glb = await new Promise((resolve, reject) => { exporter.parse( scene, (result) => resolve(result as ArrayBuffer), (error) => reject(error), { binary: true } ); }); const glbFileName = fileName.replace(/\.(obj|stl)$/i, '.glb'); const blob = new Blob([glb], { type: 'model/gltf-binary' }); return { blob, fileName: glbFileName, fileSize: blob.size }; }