Files
archived-stlTexturizer/js/exporter.js
T
Avatarsia 3c9bcfd75c perf: replace Three.js STLExporter with direct binary writer
Write binary STL directly from BufferGeometry typed arrays using
Uint8Array.set() bulk copies. Eliminates the Three.js STLExporter
overhead: no Mesh/Material creation, no identity matrix multiplication,
no redundant normal recomputation, no per-float DataView calls.
2026-04-06 02:38:58 +02:00

72 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Fast binary STL exporter — writes directly from BufferGeometry arrays.
*
* Eliminates Three.js STLExporter overhead:
* - No Mesh/Material creation
* - No identity matrix multiplication per vertex
* - No redundant normal recomputation
* - Bulk Uint8Array.set() instead of per-float DataView calls
*
* @param {THREE.BufferGeometry} geometry non-indexed with position + normal
* @param {string} [filename]
*/
export function exportSTL(geometry, filename = 'textured.stl') {
const posArr = geometry.attributes.position.array;
const norArr = geometry.attributes.normal
? geometry.attributes.normal.array
: null;
const triCount = (posArr.length / 9) | 0;
// Binary STL: 80-byte header + 4-byte tri count + 50 bytes per triangle
const bufLen = 84 + 50 * triCount;
const buffer = new ArrayBuffer(bufLen);
const bytes = new Uint8Array(buffer);
const view = new DataView(buffer);
// Header: 80 bytes (already zero-filled)
view.setUint32(80, triCount, true);
// Reinterpret source arrays as raw bytes for bulk copy
const posSrc = new Uint8Array(posArr.buffer, posArr.byteOffset, posArr.byteLength);
const norSrc = norArr
? new Uint8Array(norArr.buffer, norArr.byteOffset, norArr.byteLength)
: null;
for (let i = 0; i < triCount; i++) {
const dst = 84 + i * 50;
const srcOff = i * 36; // 9 floats * 4 bytes
if (norSrc) {
// Normal: copy first vertex normal (12 bytes) — flat shading, all 3 identical
bytes.set(norSrc.subarray(srcOff, srcOff + 12), dst);
} else {
// Compute face normal from cross product
const b = i * 9;
const ux = posArr[b+3]-posArr[b], uy = posArr[b+4]-posArr[b+1], uz = posArr[b+5]-posArr[b+2];
const vx = posArr[b+6]-posArr[b], vy = posArr[b+7]-posArr[b+1], vz = posArr[b+8]-posArr[b+2];
const nx = uy*vz-uz*vy, ny = uz*vx-ux*vz, nz = ux*vy-uy*vx;
const len = Math.sqrt(nx*nx + ny*ny + nz*nz) || 1;
view.setFloat32(dst, nx/len, true);
view.setFloat32(dst + 4, ny/len, true);
view.setFloat32(dst + 8, nz/len, true);
}
// Vertices: 36 bytes (3 vertices * 3 floats * 4 bytes)
bytes.set(posSrc.subarray(srcOff, srcOff + 36), dst + 12);
// Attribute byte count: 0 (already zero-filled)
}
// Download
const blob = new Blob([buffer], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 10000);
}