| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- // 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.fine<manifest.fine,'fine requests local only')
- assert.ok(zoom.selected.shell+zoom.selected.far+zoom.selected.near>0,'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}
|