| 123456789101112131415161718192021222324 |
- import assert from 'node:assert/strict'
- import {instanceSafePrimitives,decodePrimitive,rotate,safeNormalDot} from './instance-safe-primitives.mjs'
- import {packGlb,parseGlb,accessorValues} from './instance-repeated-meshes.mjs'
- import {compressInstanceBuffers,unpackInstanceBuffers} from './instance-buffer-codec.mjs'
- const bin=Buffer.alloc(84),positions=[-1,-1,0,1,-1,0,-1,1,0],normals=[0,0,1,0,0,1,0,0,1]
- positions.concat(normals).forEach((v,i)=>bin.writeFloatLE(v,i*4));[0,1,2].forEach((v,i)=>bin.writeUInt32LE(v,72+i*4))
- const json={asset:{version:'2.0'},scene:0,scenes:[{nodes:[0,1]}],nodes:[{name:'a',mesh:0,translation:[2,3,4],scale:[20,4,3],rotation:[0,0.6,0,0.8]},
- {name:'b',mesh:0,translation:[7,8,9],scale:[10,2,7]}],meshes:[{primitives:[{attributes:{POSITION:0,NORMAL:1},indices:2,material:0}]}],materials:[{name:'wall'}],buffers:[{byteLength:84}],
- bufferViews:[{buffer:0,byteOffset:0,byteLength:36},{buffer:0,byteOffset:36,byteLength:36},{buffer:0,byteOffset:72,byteLength:12}],
- accessors:[{bufferView:0,componentType:5126,count:3,type:'VEC3'},{bufferView:1,componentType:5126,count:3,type:'VEC3'},{bufferView:2,componentType:5125,count:3,type:'SCALAR'}]}
- const result=instanceSafePrimitives(packGlb(json,bin));assert.equal(result.report.instanceGroups,1);assert.equal(result.report.outputPrimitives,1)
- const compressed=compressInstanceBuffers(result.bytes),out=unpackInstanceBuffers(compressed),node=out.json.nodes[0],p=out.json.meshes[0].primitives[0],g=decodePrimitive(out.json,out.bin,p)
- assert.deepEqual(g.positions.slice(0,9),positions);assert.deepEqual(g.normals.slice(0,9),normals);assert.deepEqual(g.indices,[0,1,2]);assert.ok(Math.max(...g.indices)<node.extras.sourceVertexCount)
- const ext=node.extensions.EXT_mesh_gpu_instancing.attributes
- assert.deepEqual(accessorValues(out.json,out.bin,ext.TRANSLATION),[[2,3,4],[7,8,9]])
- const a=out.json.accessors[p.attributes.POSITION],ts=accessorValues(out.json,out.bin,ext.TRANSLATION),rs=accessorValues(out.json,out.bin,ext.ROTATION),ss=accessorValues(out.json,out.bin,ext.SCALE)
- const min=a.min.map((v,k)=>v+Math.min(...ts.map(t=>t[k]))),max=a.max.map((v,k)=>v+Math.max(...ts.map(t=>t[k]))),center=min.map((v,k)=>(v+max[k])/2),radius=Math.hypot(...min.map((v,k)=>(max[k]-v)/2))
- for(let i=0;i<ts.length;i++)for(let j=0;j<positions.length;j+=3){const q=rotate(positions.slice(j,j+3).map((v,k)=>v*ss[i][k]),rs[i]).map((v,k)=>v+ts[i][k]);assert.ok(Math.hypot(...q.map((v,k)=>v-center[k]))<=radius)}
- const sloped=Buffer.from(bin),n=[Math.SQRT1_2,Math.SQRT1_2,0];for(let i=0;i<3;i++)n.forEach((v,j)=>sloped.writeFloatLE(v,36+(i*3+j)*4))
- assert.ok(safeNormalDot(n,[1.3,1,.7])<0.999999)
- assert.equal(instanceSafePrimitives(packGlb(json,sloped)).report.instanceGroups,0,'sloped normals must retain ordinary transforms')
- const mutated=structuredClone(json);mutated.meshes[0].primitives[0].targets=[{POSITION:0}];assert.throws(()=>instanceSafePrimitives(packGlb(mutated,bin)))
- const raw=parseGlb(result.bytes);assert.deepEqual(out.bin,raw.bin,'meshopt compression must be bit-lossless')
- console.log(JSON.stringify({passed:true,scope:'safe normal invariance, exact TRS, conservative command bounds, unused vertices, lossless meshopt, slope/morph rejection'}))
|