// Installed traversal over the real index with synthetic ENU camera/readiness. // All-frustum-visible worst-case proxy. No requests sent, decoding, GPU or browser. import assert from 'node:assert/strict' import { readFileSync, writeFileSync } from 'node:fs' import { resolve, join } from 'node:path' import Traversal from '../../node_modules/@cesium/engine/Source/Scene/Cesium3DTilesetTraversal.js' import Base from '../../node_modules/@cesium/engine/Source/Scene/Cesium3DTilesetBaseTraversal.js' import Refine from '../../node_modules/@cesium/engine/Source/Scene/Cesium3DTileRefine.js' const dir=resolve(process.argv[2]),tree=JSON.parse(readFileSync(join(dir,'tileset.json'))),manifest=JSON.parse(readFileSync(join(dir,'manifest.json'))) const records=new Map(manifest.records.map(r=>[r.uri,r])),height=1080,denominator=2*Math.tan(Math.PI/6),threshold=96 const ts={_requestedTiles:[],_selectedTiles:[],_selectedTilesToStyle:[],_emptyTiles:[],memoryAdjustedScreenSpaceError:threshold, debugFreezeFrame:false,loadSiblings:false,hasExtension(){return false},_minimumPriority:{distance:Infinity,depth:Infinity,foveatedFactor:Infinity,reverseScreenSpaceError:Infinity}, _maximumPriority:{distance:0,depth:0,foveatedFactor:0,reverseScreenSpaceError:0},_statistics:{visited:0},_cache:{touch(){}},_cullRequestsWhileMoving:false} let camera,readiness,number=0 function sphere(volume){if(volume.sphere)return volume.sphere;const b=volume.box;return [...b.slice(0,3),Math.hypot(b[3],b[7],b[11])]} function make(json,parent){const s=sphere(json.boundingVolume),r=json.content?records.get(json.content.uri):null const t={tileset:ts,parent,children:[],refine:Refine.REPLACE,hasRenderableContent:!!json.content,hasEmptyContent:!json.content,contentExpired:false,_depth:parent?parent._depth+1:0, _visible:true,_inRequestVolume:true,_distanceToCamera:1,_foveatedFactor:0,_priorityReverseScreenSpaceError:0,_selectedFrame:-1, get isVisible(){return this._visible&&this._inRequestVolume},getScreenSpaceError(){return this._screenSpaceError},updateExpiration(){},updatePriority(){}, content:{featurePropertiesDirty:false},contentVisibility(){return 1},record:r, updateVisibility(){const dist=Math.hypot(...s.slice(0,3).map((v,a)=>v-camera[a]));this._distanceToCamera=Math.max(0.01,dist-s[3]);this._screenSpaceError=json.geometricError*height/(this._distanceToCamera*denominator) this._visible=true;const gate=json.viewerRequestVolume?.sphere;this._inRequestVolume=!gate||Math.hypot(...gate.slice(0,3).map((v,a)=>v-camera[a]))<=gate[3] this.contentAvailable=!!r&&readiness.has(r.level);this.hasUnloadedRenderableContent=!!r&&!this.contentAvailable}} t.children=(json.children||[]).map(c=>make(c,t));return t} ts.root=make(tree.root,null) const original=Traversal.selectTile;Traversal.selectTile=t=>ts._selectedTiles.push(t) import { parseGlb } from './instance-repeated-meshes.mjs' const resourceStats=new Map() function resources(list){const total={files:0,triangles:0,vertices:0,primitives:0,bytes:0} for(const t of list){const uri=t.record.uri;if(!resourceStats.has(uri)){const data=readFileSync(resolve(dir,uri)),{json}=parseGlb(data),value={files:1,triangles:0,vertices:0,primitives:0,bytes:data.length};for(const n of json.nodes)if(n.mesh!==undefined)for(const p of json.meshes[n.mesh].primitives){value.triangles+=json.accessors[p.indices].count/3;value.vertices+=json.accessors[p.attributes.POSITION].count;value.primitives++}resourceStats.set(uri,value)}const value=resourceStats.get(uri);for(const k of Object.keys(total))total[k]+=value[k]} return total } function sample(label,pose,loaded){camera=pose;readiness=new Set(loaded);Base.selectTiles(ts,{frameNumber:++number,camera:{timeSinceMoved:1,positionWCDeltaMagnitude:0,positionWCDeltaMagnitudeLastFrame:0}}) const tally=list=>list.reduce((o,t)=>{const k=t.record?.level||'empty';o[k]=(o[k]||0)+1;return o},{shell:0,far:0,near:0,fine:0}) return {label,cameraENU:pose,readyLevels:loaded,requested:tally(ts._requestedTiles),selected:tally(ts._selectedTiles),selectedResources:resources(ts._selectedTiles)} } try{ const overview=sample('overview cold',[0,-1000,10000],[]) assert.equal(overview.requested.fine,0,'overview must not request citywide fine') assert.ok(overview.requested.far<=530&&overview.requested.near<200,'cold overview requests bounded coarse, not all3022parts') const waitingNear=sample('near, local coarse pending',[0,-1000,350],manifest.shellFiles?['shell']:['far']) if(manifest.shellFiles)assert.equal(waitingNear.selected.shell,manifest.shellFiles,'light shell must survive pending coarse descendants') else assert.equal(waitingNear.selected.far,manifest.far,'far content must survive pending near descendants') assert.equal(waitingNear.selected.near,0) const zoom=sample('near, fine pending',[0,-1000,350],['shell','far','near']) assert.ok(zoom.requested.fine>0&&zoom.requested.fine0,'pending fine needs coarse fallback') const ready=sample('near, fine ready',[0,-1000,350],['shell','far','near','fine']) assert.ok(ready.selected.fine>0&&ready.selected.shell+ready.selected.far+ready.selected.near>0,'local fine plus distant coarse coexist') const back=sample('overview again',[0,-1000,10000],['shell','far','near','fine']) assert.equal(back.selected.fine,0) const result={passed:true,actualCesiumBaseTraversal:true,cameraMath:'synthetic sphere-distance SSE at1080p60deg, every tile treated frustum-visible',samples:[overview,waitingNear,zoom,ready,back],requestsSent:false,renderingValidated:false,fpsValidated:false} if(process.argv[3])writeFileSync(process.argv[3],JSON.stringify(result,null,2));console.log(JSON.stringify(result)) }finally{Traversal.selectTile=original}