import assert from 'node:assert/strict' import {MeshoptEncoder,MeshoptDecoder} from 'meshoptimizer' import {parseGlb,packGlb} from './instance-repeated-meshes.mjs' await Promise.all([MeshoptEncoder.ready,MeshoptDecoder.ready]) export function compressInstanceBuffers(bytes){ const {json,bin}=parseGlb(bytes),dracoViews=new Set(json.meshes.flatMap(m=>m.primitives.map(p=>p.extensions?.KHR_draco_mesh_compression?.bufferView)).filter(i=>i!==undefined)) const chunks=[];let size=0,fallback=0 for(let i=0;ia.bufferView===i) const pad=(-size)&3;if(pad){chunks.push(Buffer.alloc(pad));size+=pad} if(dracoViews.has(i)||!a){view.byteOffset=size;view.buffer=0;chunks.push(data);size+=data.length;continue} assert.equal(a.componentType===5125||a.componentType===5126,true);assert.equal(a.byteOffset||0,0) const stride={SCALAR:4,VEC2:8,VEC3:12,VEC4:16}[a.type],count=data.length/stride,mode=a.componentType===5125?'INDICES':'ATTRIBUTES' assert.equal(count,a.count) const encoded=Buffer.from(MeshoptEncoder.encodeGltfBuffer(data,count,stride,mode)) view.extensions={EXT_meshopt_compression:{buffer:0,byteOffset:size,byteLength:encoded.length,byteStride:stride,count,mode,filter:'NONE'}} view.buffer=1;view.byteOffset=fallback;fallback+=data.length chunks.push(encoded);size+=encoded.length } json.extensionsUsed=[...new Set([...(json.extensionsUsed||[]),'EXT_meshopt_compression'])] json.extensionsRequired=[...new Set([...(json.extensionsRequired||[]),'EXT_meshopt_compression'])] json.buffers=[{byteLength:size},{byteLength:fallback,extensions:{EXT_meshopt_compression:{fallback:true}}}] return packGlb(json,Buffer.concat(chunks)) } export function unpackInstanceBuffers(bytes){ const {json,bin}=parseGlb(bytes),chunks=[];let size=0 for(const view of json.bufferViews){const pad=(-size)&3;if(pad){chunks.push(Buffer.alloc(pad));size+=pad} const ext=view.extensions?.EXT_meshopt_compression;let data if(ext){data=Buffer.alloc(ext.count*ext.byteStride);MeshoptDecoder.decodeGltfBuffer(data,ext.count,ext.byteStride,bin.subarray(ext.byteOffset,ext.byteOffset+ext.byteLength),ext.mode,ext.filter);delete view.extensions} else data=bin.subarray(view.byteOffset||0,(view.byteOffset||0)+view.byteLength) view.byteOffset=size;view.buffer=0;chunks.push(data);size+=data.length } json.buffers=[{byteLength:size}];json.extensionsUsed=(json.extensionsUsed||[]).filter(e=>e!=='EXT_meshopt_compression');json.extensionsRequired=(json.extensionsRequired||[]).filter(e=>e!=='EXT_meshopt_compression') return {json,bin:Buffer.concat(chunks)} }