| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- // Static LOD gate: validates spatial cells, REPLACE hierarchy, nonempty content and LOD byte/triangle reduction.
- import { readFileSync, statSync } from 'node:fs'
- import { resolve, dirname, join } from 'node:path'
- const dir = resolve(process.argv[2] || 'tools/city-massing/district/lod-preview'),
- tile = JSON.parse(readFileSync(join(dir, 'tileset.json')), 'utf8'),
- manifest = JSON.parse(readFileSync(join(dir, 'manifest.json'), 'utf8'))
- const fail = []
- const root = tile.root || {}
- if (!Array.isArray(root.children) || root.children.length < 2) fail.push('root has fewer than two spatial children')
- if (!root.content?.uri) fail.push('root must provide a valid GLB anchor for runtime traversal')
- else {
- const anchor = readFileSync(join(dir, root.content.uri))
- if (anchor.toString('ascii', 0, 4) !== 'glTF' || anchor.readUInt32LE(4) !== 2 ||
- anchor.readUInt32LE(8) !== anchor.length || anchor.toString('ascii', 16, 20) !== 'JSON') {
- fail.push('root anchor GLB has invalid header')
- } else {
- try {
- JSON.parse(anchor.toString('utf8', 20, 20 + anchor.readUInt32LE(12)))
- } catch {
- fail.push('root anchor GLB has invalid JSON chunk')
- }
- }
- }
- const cellUris = []
- for (const cell of root.children || []) {
- if (cell.refine !== 'REPLACE') fail.push('cell lacks REPLACE refinement')
- if (!cell.content?.uri) fail.push('cell lacks LOD1 content')
- else cellUris.push(cell.content.uri)
- if (cell.geometricError <= 0) fail.push('LOD1 cell geometricError must be positive')
- if (!Array.isArray(cell.children) || cell.children.length < 1) {
- fail.push('cell lacks LOD0 children')
- continue
- }
- for (const leaf of cell.children) {
- if (leaf.geometricError !== 0) fail.push('LOD0 leaf geometricError must be zero')
- if (!leaf.content?.uri) fail.push('LOD0 leaf lacks content')
- else cellUris.push(leaf.content.uri)
- }
- }
- for (const uri of cellUris) {
- if (!statSync(join(dir, uri), { throwIfNoEntry: false })) fail.push(`missing content ${uri}`)
- }
- const totals = manifest.totals || {}
- if (!(totals.lod1?.bytes < totals.lod0?.bytes)) fail.push('LOD1 bytes are not below LOD0')
- if (!(totals.lod1?.triangles < totals.lod0?.triangles)) fail.push('LOD1 triangles are not below LOD0')
- if (manifest.count <= 0 || manifest.cellCount < 2) fail.push('manifest is empty')
- if (fail.length) throw new Error(fail.join('\n'))
- console.log(
- JSON.stringify({
- passed: true,
- scope: manifest.scope,
- cells: manifest.cellCount,
- count: manifest.count,
- low: totals.lod1,
- high: totals.lod0,
- })
- )
|