diff --git a/src/lib/exportSelectionGLB.ts b/src/lib/exportSelectionGLB.ts new file mode 100644 index 0000000..1b91297 --- /dev/null +++ b/src/lib/exportSelectionGLB.ts @@ -0,0 +1,46 @@ +import * as THREE from 'three'; +import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'; + +/** + * Export the given root objects (already cloned or live in scene) as a single + * binary GLB, preserving each one's current world transform. Triggers a + * browser download. + */ +export async function exportObjectsAsGLB( + objects: THREE.Object3D[], + fileName: string +): Promise { + if (objects.length === 0) return; + + const scene = new THREE.Scene(); + for (const src of objects) { + src.updateWorldMatrix(true, false); + const clone = src.clone(true); + // Bake world matrix into the clone so the export is position-correct + // regardless of parent chain. + clone.matrix.copy(src.matrixWorld); + clone.matrix.decompose(clone.position, clone.quaternion, clone.scale); + clone.matrixAutoUpdate = true; + scene.add(clone); + } + + const exporter = new GLTFExporter(); + const glb = await new Promise((resolve, reject) => { + exporter.parse( + scene, + (res) => resolve(res as ArrayBuffer), + (err) => reject(err), + { binary: true } + ); + }); + + const blob = new Blob([glb], { type: 'model/gltf-binary' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName.endsWith('.glb') ? fileName : `${fileName}.glb`; + document.body.appendChild(a); + a.click(); + a.remove(); + setTimeout(() => URL.revokeObjectURL(url), 2000); +}