| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // Real render regression using the application engine and an entirely buried red 3D Tiles cube.
- // Run with COCKPIT_BASE and OCCLUSION_TERRAIN=local|ellipsoid; /gis/model-review mounts this directory.
- import assert from 'node:assert/strict'
- import { mkdirSync, writeFileSync } from 'node:fs'
- import { resolve } from 'node:path'
- import { PNG } from 'pngjs'
- import { Cartesian3, Transforms, Matrix4 } from 'cesium'
- const BASE = process.env.MODEL_REVIEW_BASE || 'http://127.0.0.1:8092'
- const USE_DEM = process.env.OCCLUSION_TERRAIN === 'local'
- const OUT = resolve('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/occlusion' + (USE_DEM ? '-dem' : ''))
- const FIXTURE = resolve('tools/city-massing/district/occlusion-fixture')
- mkdirSync(OUT, { recursive: true })
- mkdirSync(FIXTURE, { recursive: true })
- const p = [
- -40, -110, -40, 40, -110, -40, 40, -30, -40, -40, -30, -40, -40, -110, 40, 40, -110, 40, 40, -30, 40, -40, -30, 40,
- ]
- const ix = [0, 1, 2, 0, 2, 3, 4, 6, 5, 4, 7, 6, 0, 4, 5, 0, 5, 1, 3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 1, 5, 6, 1, 6, 2]
- const bin = Buffer.concat([Buffer.from(new Float32Array(p).buffer), Buffer.from(new Uint16Array(ix).buffer)])
- const doc = {
- asset: { version: '2.0' },
- scene: 0,
- scenes: [{ nodes: [0] }],
- nodes: [{ mesh: 0 }],
- meshes: [{ primitives: [{ attributes: { POSITION: 0 }, indices: 1, material: 0 }] }],
- materials: [
- {
- extensions: { KHR_materials_unlit: {} },
- pbrMetallicRoughness: { baseColorFactor: [1, 0, 0, 1] },
- doubleSided: true,
- },
- ],
- extensionsUsed: ['KHR_materials_unlit'],
- buffers: [{ byteLength: bin.length }],
- bufferViews: [
- { buffer: 0, byteOffset: 0, byteLength: 96 },
- { buffer: 0, byteOffset: 96, byteLength: 72 },
- ],
- accessors: [
- { bufferView: 0, componentType: 5126, count: 8, type: 'VEC3', min: [-40, -110, -40], max: [40, -30, 40] },
- { bufferView: 1, componentType: 5123, count: 36, type: 'SCALAR' },
- ],
- }
- const txt = Buffer.from(JSON.stringify(doc))
- const js = Buffer.concat([txt, Buffer.alloc((4 - (txt.length % 4)) % 4, 32)])
- const header = Buffer.alloc(20)
- header.write('glTF')
- header.writeUInt32LE(2, 4)
- header.writeUInt32LE(28 + js.length + bin.length, 8)
- header.writeUInt32LE(js.length, 12)
- header.write('JSON', 16)
- const bh = Buffer.alloc(8)
- bh.writeUInt32LE(bin.length)
- bh.write('BIN\0', 4)
- writeFileSync(resolve(FIXTURE, 'red.glb'), Buffer.concat([header, js, bh, bin]))
- writeFileSync(
- resolve(FIXTURE, 'tileset.json'),
- JSON.stringify({
- asset: { version: '1.1' },
- geometricError: 1000,
- root: {
- transform: Matrix4.toArray(
- Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(110.39028164, 28.4436787))
- ),
- boundingVolume: { box: [0, 0, -70, 40, 0, 0, 0, 40, 0, 0, 0, 40] },
- geometricError: 0,
- content: { uri: 'red.glb' },
- },
- })
- )
- const puppeteer = (
- await import(
- 'file:///C:/Users/ASUS/.dsh/profiles/desktop/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js'
- )
- ).default
- const browser = await puppeteer.launch({
- executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe',
- headless: true,
- defaultViewport: { width: 600, height: 600 },
- args: ['--no-sandbox'],
- })
- const results = []
- try {
- const page = await browser.newPage()
- await page.goto(BASE + '/preview.html#automation', { waitUntil: 'domcontentloaded' })
- await page.waitForFunction(() => typeof window.startPreview === 'function')
- await page.evaluate(
- (dem) =>
- window.startPreview({
- model: '/review-assets/district/occlusion-fixture/tileset.json',
- terrain: dem,
- imagery: false,
- }),
- USE_DEM
- )
- await page.evaluate(() => {
- const { v, C } = window.scene35
- v.camera.setView({
- destination: C.Cartesian3.fromDegrees(110.39028164, 28.4436787, 750),
- orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
- })
- document.querySelector('#caption').style.display = 'none'
- })
- await page.waitForFunction(() => window.scene35.v.scene.globe._surface._tilesToRender.length > 0, {
- timeout: 45000,
- })
- async function capture(name, mode) {
- const state = await page.evaluate((mode) => {
- const { v, E, tiles } = window.scene35
- if (mode === 'off') v.scene.globe.depthTestAgainstTerrain = false
- if (mode === 'on') v.scene.globe.depthTestAgainstTerrain = true
- if (mode === 'ground') E.applyViewModeLayers(v, tiles, { mode: 'ground', terrainAlpha: 1, tilesetAlpha: 1 })
- if (mode === 'pipeline')
- E.applyViewModeLayers(v, tiles, { mode: 'pipeline', terrainAlpha: 0.45, tilesetAlpha: 0.4 })
- return { depth: v.scene.globe.depthTestAgainstTerrain, translucent: v.scene.globe.translucency.enabled }
- }, mode)
- await page.waitForFunction(() => window.scene35.tiles[0].root.content != null, { timeout: 30000 })
- const frames = await page.evaluate(() => window.frameCount)
- await page.waitForFunction((f) => window.frameCount > f + 40, {}, frames)
- const im = PNG.sync.read(Buffer.from(await page.screenshot({ path: resolve(OUT, name + '.png') })))
- let red = 0
- for (let y = 190; y < 410; y++)
- for (let x = 190; x < 410; x++) {
- const i = (y * im.width + x) * 4
- if (im.data[i] > 100 && im.data[i] > im.data[i + 1] * 1.8 && im.data[i] > im.data[i + 2] * 1.8) red++
- }
- const row = { name, ...state, redPixels: red }
- results.push(row)
- console.log(JSON.stringify(row))
- return red
- }
- const initial = await capture('01-actual-initial', 'initial')
- const off = await capture('02-control-depth-off', 'off')
- const on = await capture('03-control-depth-on', 'on')
- const pipe = await capture('04-pipeline', 'pipeline')
- const ground = await capture('05-ground-return', 'ground')
- writeFileSync(
- resolve(OUT, 'report.json'),
- JSON.stringify({ terrain: USE_DEM ? 'local-dem' : 'ellipsoid', results }, null, 2)
- )
- assert(off > 100, 'Positive control must show buried model with depth off')
- assert(on < 5, 'Depth-on must hide buried model')
- assert(initial < 5, 'Actual createViewer must not see through terrain')
- assert(pipe > 100, 'Explicit pipeline translucency must permit viewing below ground')
- assert(ground < 5, 'Ground mode must restore terrain occlusion')
- console.log('PASS actual terrain depth, negative control, pipeline and return')
- } finally {
- await browser.close()
- }
|