verify-progressive-instanced-city.mjs 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import assert from 'node:assert/strict'
  2. import {readFileSync,writeFileSync,existsSync} from 'node:fs'
  3. import {resolve,join} from 'node:path'
  4. import {parseGlb} from './instance-repeated-meshes.mjs'
  5. const dir=resolve(process.argv[2]),m=JSON.parse(readFileSync(join(dir,'manifest.json'))),tree=JSON.parse(readFileSync(join(dir,'tileset.json')))
  6. const ids=new Set(),uris=new Set(),declared=new Map(m.records.map(r=>[resolve(dir,r.uri),r]))
  7. let content=0,maxTriangles=0,instancedGroups=0,instancedCount=0,far=0,near=0,fine=0
  8. for(const d of m.decisions){
  9. for(const id of d.buildingIds){assert.ok(!ids.has(id),'duplicate ownership '+id);ids.add(id)}
  10. if(d.useInstancing){assert.ok(d.candidateBytes<d.oldBytes);assert.ok(d.candidatePrimitives<=d.oldPrimitives)}
  11. }
  12. function check(t,parent=null){
  13. if(t.children?.length){assert.equal(t.refine,'REPLACE');assert.ok(t.boundingVolume.sphere||t===tree.root);assert.equal(t.geometricError,1000000)}
  14. if(t.content){
  15. const path=resolve(dir,t.content.uri);assert.ok(existsSync(path),path)
  16. assert.ok(!uris.has(path),'repeated GLB '+path);uris.add(path)
  17. const r=declared.get(path);assert.ok(r,'undeclared content '+path);content++
  18. if(r.level==='far')far++;else if(r.level==='near')near++;else fine++
  19. const {json,bin}=parseGlb(readFileSync(path));let triangles=0,vertices=0
  20. for(const n of json.nodes){
  21. if(n.mesh===undefined)continue
  22. const ext=n.extensions?.EXT_mesh_gpu_instancing
  23. const count=ext?json.accessors[ext.attributes.TRANSLATION].count:1
  24. if(ext){
  25. instancedGroups++;instancedCount+=count
  26. const rotations=ext.attributes.ROTATION!==undefined
  27. const scales=ext.attributes.SCALE!==undefined
  28. assert.equal(rotations,scales,'rotation/scale must be paired')
  29. if(!rotations) assert.ok(ext.attributes.TRANSLATION!==undefined)
  30. }
  31. for(const p of json.meshes[n.mesh].primitives){triangles+=json.accessors[p.indices].count/3*count;vertices+=json.accessors[p.attributes.POSITION].count*count}
  32. }
  33. maxTriangles=Math.max(maxTriangles,triangles);assert.ok(triangles<=20000,path+' triangles');assert.ok(vertices<=60000,path+' vertices')
  34. }
  35. if(parent?.viewerRequestVolume&&parent.children?.length&& !parent.content){
  36. // Shared local coarse gate: all non-leaf children are admitted together.
  37. if(t.children?.length)assert.deepEqual(t.viewerRequestVolume,parent.viewerRequestVolume)
  38. }
  39. if(t.children?.length&&t.content){
  40. const parentRecord=declared.get(resolve(dir,t.content.uri))
  41. if(parentRecord?.level==='near')for(const c of t.children){
  42. assert.ok(c.content,'fine counterpart missing')
  43. const childRecord=declared.get(resolve(dir,c.content.uri))
  44. assert.ok(childRecord&&childRecord.level==='fine')
  45. assert.deepEqual([...childRecord.buildingIds].sort(),[...parentRecord.buildingIds].sort(),'coarse/fine ownership differs')
  46. }
  47. }
  48. for(const c of t.children||[])check(c,t)
  49. }
  50. check(tree.root)
  51. assert.equal(ids.size,30172);assert.equal(content,m.records.length);assert.equal(uris.size,declared.size)
  52. assert.equal(far,m.farFiles);assert.equal(near,m.nearFiles);assert.equal(fine,m.fineFiles)
  53. // Every near branch must have a fine child and both URI refs must exist, including fallbacks.
  54. for(const d of m.decisions)assert.ok(m.records.some(r=>r.sourceCell===d.cell),'missing reachable cell '+d.cell)
  55. const result={passed:true,buildings:ids.size,content,far,near,fine,maxRenderedTriangles:maxTriangles,instancedGroups,instancedCount,
  56. instanceCells:m.instancedCells,instancedBuildings:m.instancedBuildings,allFallbacksReachable:true,
  57. coverageValidated:true,renderingValidated:false,fpsValidated:false}
  58. if(process.argv[3])writeFileSync(process.argv[3],JSON.stringify(result,null,2))
  59. console.log(JSON.stringify(result))