| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- import assert from 'node:assert/strict'
- import {readFileSync,writeFileSync,existsSync} from 'node:fs'
- import {resolve,join} from 'node:path'
- import {parseGlb} from './instance-repeated-meshes.mjs'
- const dir=resolve(process.argv[2]),m=JSON.parse(readFileSync(join(dir,'manifest.json'))),tree=JSON.parse(readFileSync(join(dir,'tileset.json')))
- const ids=new Set(),uris=new Set(),declared=new Map(m.records.map(r=>[resolve(dir,r.uri),r]))
- let content=0,maxTriangles=0,instancedGroups=0,instancedCount=0,far=0,near=0,fine=0
- for(const d of m.decisions){
- for(const id of d.buildingIds){assert.ok(!ids.has(id),'duplicate ownership '+id);ids.add(id)}
- if(d.useInstancing){assert.ok(d.candidateBytes<d.oldBytes);assert.ok(d.candidatePrimitives<=d.oldPrimitives)}
- }
- function check(t,parent=null){
- if(t.children?.length){assert.equal(t.refine,'REPLACE');assert.ok(t.boundingVolume.sphere||t===tree.root);assert.equal(t.geometricError,1000000)}
- if(t.content){
- const path=resolve(dir,t.content.uri);assert.ok(existsSync(path),path)
- assert.ok(!uris.has(path),'repeated GLB '+path);uris.add(path)
- const r=declared.get(path);assert.ok(r,'undeclared content '+path);content++
- if(r.level==='far')far++;else if(r.level==='near')near++;else fine++
- const {json,bin}=parseGlb(readFileSync(path));let triangles=0,vertices=0
- for(const n of json.nodes){
- if(n.mesh===undefined)continue
- const ext=n.extensions?.EXT_mesh_gpu_instancing
- const count=ext?json.accessors[ext.attributes.TRANSLATION].count:1
- if(ext){
- instancedGroups++;instancedCount+=count
- const rotations=ext.attributes.ROTATION!==undefined
- const scales=ext.attributes.SCALE!==undefined
- assert.equal(rotations,scales,'rotation/scale must be paired')
- if(!rotations) assert.ok(ext.attributes.TRANSLATION!==undefined)
- }
- 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}
- }
- maxTriangles=Math.max(maxTriangles,triangles);assert.ok(triangles<=20000,path+' triangles');assert.ok(vertices<=60000,path+' vertices')
- }
- if(parent?.viewerRequestVolume&&parent.children?.length&& !parent.content){
- // Shared local coarse gate: all non-leaf children are admitted together.
- if(t.children?.length)assert.deepEqual(t.viewerRequestVolume,parent.viewerRequestVolume)
- }
- if(t.children?.length&&t.content){
- const parentRecord=declared.get(resolve(dir,t.content.uri))
- if(parentRecord?.level==='near')for(const c of t.children){
- assert.ok(c.content,'fine counterpart missing')
- const childRecord=declared.get(resolve(dir,c.content.uri))
- assert.ok(childRecord&&childRecord.level==='fine')
- assert.deepEqual([...childRecord.buildingIds].sort(),[...parentRecord.buildingIds].sort(),'coarse/fine ownership differs')
- }
- }
- for(const c of t.children||[])check(c,t)
- }
- check(tree.root)
- assert.equal(ids.size,30172);assert.equal(content,m.records.length);assert.equal(uris.size,declared.size)
- assert.equal(far,m.farFiles);assert.equal(near,m.nearFiles);assert.equal(fine,m.fineFiles)
- // Every near branch must have a fine child and both URI refs must exist, including fallbacks.
- for(const d of m.decisions)assert.ok(m.records.some(r=>r.sourceCell===d.cell),'missing reachable cell '+d.cell)
- const result={passed:true,buildings:ids.size,content,far,near,fine,maxRenderedTriangles:maxTriangles,instancedGroups,instancedCount,
- instanceCells:m.instancedCells,instancedBuildings:m.instancedBuildings,allFallbacksReachable:true,
- coverageValidated:true,renderingValidated:false,fpsValidated:false}
- if(process.argv[3])writeFileSync(process.argv[3],JSON.stringify(result,null,2))
- console.log(JSON.stringify(result))
|