reader.onload = function(e) {
const arrayBuffer = e.target.result;
if (name.endsWith(‘.glb’) || name.endsWith(‘.gltf’)) {
// GLB/GLTF
// create blob URL for loader
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
gltfLoader.load(url, (gltf)=>{ addToScene(gltf.scene); URL.revokeObjectURL(url); });
} else if (name.endsWith(‘.stl’)) {
const geometry = stlLoader.parse(arrayBuffer);
const mat = new THREE.MeshStandardMaterial({metalness:0.1,roughness:0.6});
const mesh = new THREE.Mesh(geometry, mat);
const group = new THREE.Group(); group.add(mesh);
addToScene(group);
} else if (name.endsWith(‘.obj’)) {
const text = new TextDecoder().decode(arrayBuffer);
const obj = objLoader.parse(text);
addToScene(obj);
} else {
alert(‘Unsupported file type: ‘ + name);
}
// create download link for original
if (currentBlobURL) URL.revokeObjectURL(currentBlobURL);
currentBlobURL = URL.createObjectURL(file);
downloadLink.href = currentBlobURL;
downloadLink.download = file.name;
downloadLink.style.display = ‘inline-block’;
};
// read as array buffer for binary loaders
if (name.endsWith(‘.obj’)) reader.readAsArrayBuffer(file); else reader.readAsArrayBuffer(file);
}
fileInput.addEventListener(‘change’, (e)=>{ if (e.target.files && e.target.files.length) loadFromFile(e.target.files[0]); });
// drag & drop
container.addEventListener(‘dragover’, (ev)=>{ ev.preventDefault(); container.style.outline = ‘2px dashed rgba(255,255,255,0.06)’; });
container.addEventListener(‘dragleave’, ()=>{ container.style.outline = ‘none’; });
container.addEventListener(‘drop’, (ev)=>{ ev.preventDefault(); container.style.outline = ‘none’; if (ev.dataTransfer.files && ev.dataTransfer.files.length) loadFromFile(ev.dataTransfer.files[0]); });
btnSample.addEventListener(‘click’, ()=>{
const geom = new THREE.BoxGeometry(1,1,1);
const mat = new THREE.MeshStandardMaterial({metalness:0.2,roughness:0.4});
const mesh = new THREE.Mesh(geom, mat);
const g = new THREE.Group(); g.add(mesh);
addToScene(g);
// hide download (no original file)
if (currentBlobURL) { URL.revokeObjectURL(currentBlobURL); currentBlobURL=null; }
downloadLink.style.display=’none’;
});
btnReset.addEventListener(‘click’, ()=>{ camera.position.set(2.5,2,4); controls.target.set(0,0,0); controls.update(); });
// animation
function animate(){ requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); }
animate();
// initial size
resize();
// make canvas fill panel
// already handled by resize on window event; also observe container changes
const ro = new ResizeObserver(resize); ro.observe(container);