/** Minimal glTF 2.0 container I/O, independent of either rendering backend. */ export function parseGLB(bytes){ if(!(bytes instanceof Uint8Array)||bytes.byteLength<28)throw Error('Invalid GLB length'); const v=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength); if(v.getUint32(0,true)!==0x46546c67||v.getUint32(4,true)!==2)throw Error('Expected glTF 2.0'); if(v.getUint32(8,true)!==bytes.byteLength)throw Error('GLB length mismatch'); let gltf=null,binary=null,off=12; while(offbytes.length)throw Error('Invalid GLB chunk');const size=v.getUint32(off,true),type=v.getUint32(off+4,true);if(size%4||off+8+size>bytes.length)throw Error('Invalid GLB chunk bounds'); const part=bytes.subarray(off+8,off+8+size);if(type===0x4e4f534a)gltf=JSON.parse(new TextDecoder().decode(part));else if(type===0x004e4942)binary=part;off+=8+size;} if(!gltf||!binary)throw Error('Missing GLB data');return {gltf,binary}; } export function encodeGLB(gltf,binary){ const text=new TextEncoder().encode(JSON.stringify(gltf)),jl=(text.length+3)&~3,bl=(binary.length+3)&~3; const out=new Uint8Array(28+jl+bl),v=new DataView(out.buffer); [0x46546c67,2,out.length,jl,0x4e4f534a].forEach((n,i)=>v.setUint32(4*i,n,true));out.fill(32,20,20+jl);out.set(text,20); v.setUint32(20+jl,bl,true);v.setUint32(24+jl,0x004e4942,true);out.set(binary,28+jl);return out; }