build-far-parent-city-tileset.mjs 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. // Rejected prototype: historical coarse budgets and URI-only mapping are unsafe.
  2. // Kept as diagnostic history only; use build-far-shell-city.mjs for actual output.
  3. throw new Error('Rejected prototype; use build-far-shell-city.mjs')
  4. import assert from 'node:assert/strict'
  5. import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'
  6. import { resolve, join, relative } from 'node:path'
  7. const root=resolve('tools/city-massing/city/cad-bim'),source=join(root,'city-continuous-v8'),farDir=join(root,'far-roofed2'),out=join(root,process.argv[2]||'city-continuous-v9')
  8. assert.ok(!existsSync(out),'immutable generation')
  9. const farTree=JSON.parse(readFileSync(join(farDir,'tileset.json'))),sourceTree=JSON.parse(readFileSync(join(source,'tileset.json')))
  10. let transform=farTree.root.transform;assert.deepEqual(transform,sourceTree.root.transform)
  11. const byCell=new Map()
  12. function indexV8(t){
  13. const uri=t.content?.uri
  14. if(uri&&/-lod1\.glb$/.test(uri))byCell.set(uri.split('/').at(-1),t)
  15. for(const c of t.children||[])indexV8(c)
  16. }
  17. indexV8(sourceTree.root)
  18. const children=[],used=new Set(),records=[]
  19. function adaptLocal(tile){
  20. const t=structuredClone(tile)
  21. if(t.content)t.content.uri=relative(out,resolve(source,t.content.uri)).replaceAll('\\','/')
  22. if(t.children?.length){t.boundingVolume=t.boundingVolume.box?{sphere:[...t.boundingVolume.box.slice(0,3),Math.hypot(t.boundingVolume.box[3],t.boundingVolume.box[7],t.boundingVolume.box[11])]}:t.boundingVolume;t.geometricError=1000000;t.refine='REPLACE';delete t.viewerRequestVolume;t.children=t.children.map(adaptLocal)}
  23. return t
  24. }
  25. for(const farChild of farTree.root.children){
  26. const uri=farChild.content.uri, match=uri.match(/\.\.\/([^/]+)\/(.*)$/);assert.ok(match)
  27. const batchDir=join(root,match[1]),batchTree=JSON.parse(readFileSync(join(batchDir,'tileset.json'))),base=match[2].split('/').at(-1)
  28. const coarse=batchTree.root.children.find(t=>t.content?.uri?.split('/').at(-1)===base);assert.ok(coarse,base)
  29. const local=[]
  30. for(const child of coarse.children||[]){const cellUri=child.content?.uri?.split('/').at(-1);const branch=cellUri&&byCell.get(cellUri);if(!branch)continue;assert.ok(!used.has(cellUri),cellUri);used.add(cellUri);const cloned=adaptLocal(branch);cloned.geometricError=48;cloned.refine='REPLACE';delete cloned.viewerRequestVolume;local.push(cloned)}
  31. const center=farChild.boundingVolume.box.slice(0,3),radius=Math.hypot(farChild.boundingVolume.box[3],farChild.boundingVolume.box[7],farChild.boundingVolume.box[11])
  32. const parent={id:farChild.id,boundingVolume:{sphere:[...center,radius]},geometricError:128,refine:'REPLACE',viewerRequestVolume:{sphere:[...center,radius+14000]},content:{uri:relative(out,resolve(farDir,farChild.content.uri)).replaceAll('\\','/')},children:local}
  33. children.push(parent);records.push({id:farChild.id,localCells:local.length,farTriangles:farChild.geometricError})
  34. }
  35. // Near-estate has no historical far parent; append its already verified v8 branch.
  36. for(const t of sourceTree.root.children){const uri=t.content?.uri||'';if(uri.includes('lod-near-estate-pilot-v6'))children.push(adaptLocal(t))}
  37. assert.equal(used.size,3020,'legacy far ownership should cover the original city cells')
  38. assert.equal(children.filter(t=>t.content?.uri?.includes('far-roofed2')).length,74)
  39. mkdirSync(out,{recursive:true});writeFileSync(join(out,'root-anchor.glb'),readFileSync(join(farDir,'root-anchor.glb')))
  40. const box=farTree.root.boundingVolume
  41. writeFileSync(join(out,'tileset.json'),JSON.stringify({asset:{version:'1.1',generator:'74 coarse far parents with exact source-cell local branches'},geometricError:1000000,root:{transform,boundingVolume:box,geometricError:1000000,refine:'REPLACE',content:{uri:'root-anchor.glb'},children}},null,2)+'\n')
  42. writeFileSync(join(out,'manifest.json'),JSON.stringify({buildings:30172,farParents:74,legacyCells:used.size,nearEstate:true,records,geometryChanged:false,coarseBudgetException:'Legacy far coarse GLBs are retained only as distant parents; their historical triangles exceed the current local content budget. Local branches remain current bounded tiles.',policy:'Exact coarse-child URI to source-cell ownership; local branch GE48 under far parent GE128; fine v8 leaves preserve local ownership and parent fallback.',runtimeValidated:false},null,2)+'\n')
  43. console.log(JSON.stringify({out,farParents:74,legacyCells:used.size,nearEstate:true}))