mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Draw CCTV and data packs through engine.meshes
Add indexed world triangles to the required globe contract on Cesium, Navara and the fake engine. Own geometry, textures, visibility, depth tests, pick identities and teardown inside each engine. Reused canvas images request another frame until their new pixels upload. Move obstruction rays and owned mesh, line and mover exclusions into the Cesium surface adapter. Draw CCTV monitor planes and viewshed volumes through shared mesh handles. Draw data-pack points, straight lines, raised polygons, image rectangles and media captions through movers, lines and meshes. Delete the feature renderers for each engine and retain decoded assets across scene rebinds. Register the modules, dependency, format scope and reduced coupling baseline. Cesium is the reference for geometry, colours, image placement, marker sizes, caption offsets, picking and resource ownership. Navara now uses straight pack lines and per-position polygon heights without edge densification. Navara meshes draw in the transparent pass after the effects, with the scene-depth test and the view's exposure undone, as the pack meshes did: in the scene pass the landscape look's exposure washed a red pack image to white. The scene-depth helper moves into the Navara engine, and the stems helper that only its test still used is deleted. The mesh picker takes the globe when Navara's depth read answers with the camera itself, which it does right after another click and which hid a polygon from a quick second click. The 256 px media caption is not pickable: its transparent square took clicks meant for nearby pack features, and no action targets it. Annotation drapes remain on their existing renderers because bounded building classification, animated fills and wire cages need a separate contract extension to preserve Cesium behaviour. Coupling falls from 344 to 210 Cesium layer-renderer lines, 1294 to 794 Navara layer-renderer lines, 9 to 7 external Cesium import files, 1026 to 1005 viewer lines, 9 to 6 extension reads and 42 to 39 engine-name lines. Other counts do not grow. The full unit suite, import directions, package boundaries, formatting and coupling checks pass. No user-visible strings change. In the browser, keyless: the contract suite passes on Navara and on Cesium apart from the camera-events check that also fails intermittently before this change; CCTV v2 (58 checks), the CCTV and director engine switches pass, with the pack image at the same pixel as before on both engines. The mesh contract fixture now sits at 3 km, above the terrain Navara draws there. The director interactions script starts at its view in the link so the app's arrival flight no longer lands after the shot, and waits for the click's asynchronous pick; its last step, the next-shot action, fails the same way before this change.
This commit is contained in:
1 parent
3907b7084e
commit
f4b83a3a31
57 files changed
+1891
-1588
No files matched your search
@@ -85,7 +85,7 @@ const cesiumLayerRenderer = (file) =>
|
||||
/\/render\/cesium(?:[^/]*|\/[^/]+)\.js$/.test(file);
|
||||
// Navara renderers outside a layer's render/ directory: like those, they
|
||||
// reach the app only through the Navara mode's import().
|
||||
const NAVARA_SCENE_RENDERERS = new Set(['src/scenes/dataPacks/navara.js']);
|
||||
const NAVARA_SCENE_RENDERERS = new Set();
|
||||
// The modules that load Navara renderers, each through import().
|
||||
const NAVARA_RENDERER_LOADERS = new Set([
|
||||
'src/app/navaraMode.js',
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"metrics": {
|
||||
"cesiumLayerRendererLines": 344,
|
||||
"navaraLayerRendererLines": 1294,
|
||||
"cesiumImportFiles": 9,
|
||||
"viewerLines": 1026,
|
||||
"cesiumLayerRendererLines": 210,
|
||||
"navaraLayerRendererLines": 794,
|
||||
"cesiumImportFiles": 7,
|
||||
"viewerLines": 1005,
|
||||
"navaraKindChecks": 2,
|
||||
"engineSniffCalls": 0,
|
||||
"engineExtensionReads": 9,
|
||||
"engineExtensionReads": 6,
|
||||
"groundEffectPasses": 1,
|
||||
"cesiumSdkLoads": 3,
|
||||
"screenSpaceHandlerFiles": 4,
|
||||
"engineNameLines": 42
|
||||
"engineNameLines": 39
|
||||
},
|
||||
"measuredAt": "Move Cesium internals into the engine and draw imported records through shared primitives"
|
||||
"measuredAt": "engine.meshes: CCTV and data packs on one primitive; dead Navara stems helper removed"
|
||||
}
|
||||
@@ -1231,8 +1231,6 @@
|
||||
"src/layers/bikeshare/presentation.test.mjs",
|
||||
"src/layers/bikeshare/selectionModel.js",
|
||||
"src/layers/cctv/projectionScheduling.test.mjs",
|
||||
"src/layers/cctv/render/navaraMeshes.js",
|
||||
"src/layers/cctv/render/navaraMeshes.test.mjs",
|
||||
"src/layers/cctv/lifecycle.test.mjs",
|
||||
"src/layers/cctv/projectionMedia.test.mjs",
|
||||
"src/layers/cctv/ingestion.test.mjs",
|
||||
@@ -1563,7 +1561,6 @@
|
||||
"src/testSupport/vesselViewerRenderer.mjs",
|
||||
"scripts/qa-wildlife-switch.mjs",
|
||||
"scripts/fixtures/wildlife.mjs",
|
||||
"src/layers/cctv/render/cesiumMeshes.js",
|
||||
"src/layers/cctv/render/display.js",
|
||||
"src/layers/cctv/render/display.test.mjs",
|
||||
"src/layers/cctv/render/displayGizmo.test.mjs",
|
||||
@@ -1673,5 +1670,15 @@
|
||||
"src/globe/cesium/sonarCommands.test.mjs",
|
||||
"src/globe/cesium/starrySky.js",
|
||||
"src/globe/cesium/zoomOutGuard.js",
|
||||
"scripts/qa-geo-import-probe.mjs"
|
||||
"scripts/qa-geo-import-probe.mjs",
|
||||
"src/globe/cesium/meshes.js",
|
||||
"src/globe/meshes.js",
|
||||
"src/globe/navara/meshes.js",
|
||||
"src/globe/navara/meshes.test.mjs",
|
||||
"src/scenes/dataPacks/display.js",
|
||||
"src/scenes/dataPacks/shapes.js",
|
||||
"src/scenes/dataPacks/shapes.test.mjs",
|
||||
"src/globe/cesium/meshes.test.mjs",
|
||||
"src/globe/meshes.test.mjs",
|
||||
"src/scenes/dataPacks/display.test.mjs"
|
||||
]
|
||||
@@ -1302,7 +1302,8 @@
|
||||
"src/director/packs/manifest.js",
|
||||
"src/scenes/dataPacks/controller.js",
|
||||
"src/scenes/dataPacks/presentation.js",
|
||||
"src/scenes/dataPacks/geometry.js",
|
||||
"src/scenes/dataPacks/display.js",
|
||||
"src/scenes/dataPacks/shapes.js",
|
||||
"src/director/interactions/document.js",
|
||||
"src/director/interactions/session.js",
|
||||
"src/scenes/interactions.js",
|
||||
@@ -1341,7 +1342,7 @@
|
||||
"src/globe/contract.js",
|
||||
"src/geo/"
|
||||
],
|
||||
"external": ["cesium", "vue", "pinia", "vue-i18n"]
|
||||
"external": ["cesium", "vue", "pinia", "vue-i18n", "earcut"]
|
||||
},
|
||||
"live-observations": {
|
||||
"exports": ["./sources/live"],
|
||||
@@ -3150,7 +3151,8 @@
|
||||
"src/director/packs/manifest.js",
|
||||
"src/scenes/dataPacks/controller.js",
|
||||
"src/scenes/dataPacks/presentation.js",
|
||||
"src/scenes/dataPacks/geometry.js",
|
||||
"src/scenes/dataPacks/display.js",
|
||||
"src/scenes/dataPacks/shapes.js",
|
||||
"src/director/interactions/document.js",
|
||||
"src/director/interactions/session.js",
|
||||
"src/scenes/interactions.js",
|
||||
@@ -3456,10 +3458,7 @@
|
||||
"src/celestialRing/view.js",
|
||||
"src/layers/shared/render/navaraPolygons.js",
|
||||
"src/layers/awareness/render/navaraPulses.js",
|
||||
"src/layers/shared/render/navaraStems.js",
|
||||
"src/layers/shared/render/navaraSceneDepth.js",
|
||||
"src/scenes/dataPacks/navara.js",
|
||||
"src/scenes/dataPacks/navaraGeometry.js",
|
||||
"src/globe/navara/sceneDepth.js",
|
||||
"src/layers/shared/render/navaraStory.js",
|
||||
"src/layers/wind/render/flowMesh.js",
|
||||
"src/layers/shared/render/navaraGroundPicks.js",
|
||||
@@ -3469,7 +3468,6 @@
|
||||
"src/layers/firms/rectangle.js",
|
||||
"src/layers/shared/render/navaraMoverBank.js",
|
||||
"src/layers/shared/render/moverBank.js",
|
||||
"src/layers/cctv/render/navaraMeshes.js",
|
||||
"src/layers/radio/screenClusters.js",
|
||||
"src/layers/shared/render/imageAtlas.js",
|
||||
"src/layers/shared/render/navaraGroundSurface.js",
|
||||
@@ -3538,7 +3536,6 @@
|
||||
"src/globe/navara/ground/pass.js",
|
||||
"src/globe/navara/ground/pack.js",
|
||||
"src/globe/navara/ground/index.js",
|
||||
"src/layers/cctv/render/cesiumMeshes.js",
|
||||
"src/layers/cctv/render/meshes.js",
|
||||
"src/layers/cctv/render/display.js",
|
||||
"src/layers/cctv/worldClick.js",
|
||||
@@ -3579,7 +3576,10 @@
|
||||
"src/globe/cesium/postStages.js",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportDisplay.js"
|
||||
"src/data/geoImportDisplay.js",
|
||||
"src/globe/cesium/meshes.js",
|
||||
"src/globe/meshes.js",
|
||||
"src/globe/navara/meshes.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -3598,7 +3598,8 @@
|
||||
"@navaramap/three",
|
||||
"@navaramap/three-default-plugin",
|
||||
"three",
|
||||
"postprocessing"
|
||||
"postprocessing",
|
||||
"earcut"
|
||||
]
|
||||
},
|
||||
"application-catalog": {
|
||||
@@ -4289,7 +4290,6 @@
|
||||
"src/layers/radio/render/display.js",
|
||||
"src/layers/transit/render/style.js",
|
||||
"src/layers/cctv/render/style.js",
|
||||
"src/layers/cctv/render/cesiumMeshes.js",
|
||||
"src/layers/cctv/render/meshes.js",
|
||||
"src/layers/cctv/render/display.js",
|
||||
"src/layers/cctv/worldClick.js",
|
||||
@@ -4322,7 +4322,6 @@
|
||||
"src/layers/wind/render/navaraFlow.js",
|
||||
"src/layers/bhoteKoshi/render/display.js",
|
||||
"src/layers/shared/render/navaraStory.js",
|
||||
"src/layers/cctv/render/navaraMeshes.js",
|
||||
"src/layers/shared/render/navaraGroundSurface.js",
|
||||
"src/layers/wind/render/flowMesh.js",
|
||||
"src/layers/shared/render/navaraGroundEffect.js",
|
||||
@@ -4338,7 +4337,9 @@
|
||||
"src/styles/noir.js",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportDisplay.js"
|
||||
"src/data/geoImportDisplay.js",
|
||||
"src/globe/cesium/meshes.js",
|
||||
"src/globe/meshes.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -5100,7 +5101,9 @@
|
||||
"src/styles/surveillance.js",
|
||||
"src/styles/thermal.js",
|
||||
"src/styles/anime.js",
|
||||
"src/styles/noir.js"
|
||||
"src/styles/noir.js",
|
||||
"src/globe/cesium/meshes.js",
|
||||
"src/globe/meshes.js"
|
||||
],
|
||||
"external": ["cesium", "egm96-universal", "pbf"]
|
||||
},
|
||||
|
||||
+32
-52
@@ -294,8 +294,8 @@ function installRenderFramesHelper(page) {
|
||||
// lines named `cctv-<camera>-<role>` and `cctv-gizmo-<part>`, and the gizmo's
|
||||
// handles in the `cctv-gizmo-points` point set. engine.lines.list() names the
|
||||
// live lines (id, points, shown); their positions are what the layer's own
|
||||
// line handles hold (record.coverageEntities). The monitor plane is still a
|
||||
// Cesium plane Entity (render/cesiumMeshes.js), read as before.
|
||||
// line handles hold (record.coverageEntities). The monitor plane is a
|
||||
// world mesh, with placement held by the display's plane handle.
|
||||
const CONTRACT_SRC = `
|
||||
function cctvRecord(camId) {
|
||||
const mod = window.__godsEyeView.dataManager.layers.get('cctv').module;
|
||||
@@ -354,18 +354,15 @@ const CONTRACT_SRC = `
|
||||
);
|
||||
return shown.every(Boolean) ? 'shown' : 'hidden';
|
||||
}
|
||||
// The viewshed volumes the layer holds (record.viewshedPrimitive), with
|
||||
// their Cesium Primitive, only where that primitive is in the scene.
|
||||
// The layer's viewshed volumes whose engine meshes are still live.
|
||||
function viewshedVolumes() {
|
||||
const mod = window.__godsEyeView.dataManager.layers.get('cctv').module;
|
||||
const primitives = window.__godsEyeView.viewer.scene.primitives;
|
||||
const live = new Set(window.__godsEyeView.engine.meshes.list());
|
||||
const out = [];
|
||||
for (const record of mod._cctvRecordsForTest()) {
|
||||
const volume = record.viewshedPrimitive;
|
||||
const primitive = volume && !volume.destroyed && volume.native
|
||||
? volume.native.primitive
|
||||
: null;
|
||||
if (primitive && primitives.contains(primitive))
|
||||
const primitive = volume && !volume.destroyed ? volume.mesh : null;
|
||||
if (primitive && live.has(primitive.id))
|
||||
out.push({ camera: record.camera.id, volume, primitive });
|
||||
}
|
||||
return out;
|
||||
@@ -379,23 +376,21 @@ const CONTRACT_SRC = `
|
||||
const SERIALIZE_GEOM_SRC = `
|
||||
${CONTRACT_SRC}
|
||||
function serializeGeom(camId) {
|
||||
const viewer = window.__godsEyeView.viewer;
|
||||
const time = viewer.clock.currentTime;
|
||||
const roles = ['ray-tl', 'ray-tr', 'ray-br', 'ray-bl', 'cap'];
|
||||
const lines = liveLines();
|
||||
const poly = {};
|
||||
for (const role of roles)
|
||||
poly[role] = coverageLinePositions(camId, role, lines);
|
||||
const planeEnt = viewer.entities.getById('cctv-' + camId + '-plane');
|
||||
const planeEnt = cctvRecord(camId)?.projection?.planeEntity;
|
||||
const live = window.__godsEyeView.engine.meshes.list().includes(planeEnt?.id);
|
||||
let plane = null;
|
||||
if (planeEnt) {
|
||||
const pos = planeEnt.position.getValue(time);
|
||||
const orient = planeEnt.orientation.getValue(time);
|
||||
const dims = planeEnt.plane.dimensions.getValue(time);
|
||||
if (live) {
|
||||
const { positions, orientation: orient, shape } = planeEnt.mesh;
|
||||
const pos = positions.capCenter;
|
||||
plane = {
|
||||
position: [pos.x, pos.y, pos.z],
|
||||
orientation: [orient.x, orient.y, orient.z, orient.w],
|
||||
dimensions: [dims.x, dims.y],
|
||||
dimensions: [shape.halfW * 2, shape.halfH * 2],
|
||||
};
|
||||
}
|
||||
return { poly, plane };
|
||||
@@ -1036,7 +1031,7 @@ async function main() {
|
||||
// source. Assert the migrated surface via the host diagnostics.
|
||||
const diag = window.__gevWorldOverlay?.getDiagnostics?.();
|
||||
g.hasLabel = (diag?.entriesBySource?.['cctv-projection'] || 0) >= 1;
|
||||
const planeEnt = viewer.entities.getById('cctv-' + camId + '-plane');
|
||||
const planeEnt = cctvRecord(camId)?.projection?.planeEntity;
|
||||
g.planeShow = planeEnt ? planeEnt.show : null;
|
||||
return g;
|
||||
})(${JSON.stringify(activeId)})`);
|
||||
@@ -1056,7 +1051,7 @@ async function main() {
|
||||
.join(', '),
|
||||
);
|
||||
record(
|
||||
'plane + plane-label entities exist for the active camera',
|
||||
'plane mesh and label exist for the active camera',
|
||||
!!geomInfo.plane && geomInfo.hasLabel,
|
||||
`plane=${!!geomInfo.plane} label=${geomInfo.hasLabel} show=${geomInfo.planeShow}`,
|
||||
);
|
||||
@@ -1424,15 +1419,11 @@ async function main() {
|
||||
// never null/undefined once the runtime exists, i.e. there's no gap where
|
||||
// the plane shows nothing.
|
||||
const materialInfo = await page.evaluate((camId) => {
|
||||
const viewer = window.__godsEyeView.viewer;
|
||||
const time = viewer.clock.currentTime;
|
||||
const planeEnt = viewer.entities.getById('cctv-' + camId + '-plane');
|
||||
const mat = planeEnt?.plane?.material;
|
||||
const image = mat?.image;
|
||||
const resolved =
|
||||
image && typeof image.getValue === 'function'
|
||||
? image.getValue(time)
|
||||
: image;
|
||||
const mod = window.__godsEyeView.dataManager.layers.get('cctv').module;
|
||||
const runtime = mod._cctvRecordsForTest().find((r) => r.camera.id === camId)?.projection;
|
||||
const planeEnt = runtime?.planeEntity;
|
||||
const mat = runtime?.planeMaterial;
|
||||
const resolved = mat?.image;
|
||||
return {
|
||||
hasMaterial: !!mat,
|
||||
hasImage: !!resolved,
|
||||
@@ -1863,9 +1854,7 @@ async function main() {
|
||||
console.log(
|
||||
'Checking viewshed mode (coverage tri-state + color-coded volumes)...',
|
||||
);
|
||||
// The volumes are the layer's (record.viewshedPrimitive), each a Cesium
|
||||
// Primitive in scene.primitives (render/cesiumMeshes.js); they carry no
|
||||
// tag of their own any more.
|
||||
// Every layer volume owns one engine mesh.
|
||||
const countVolumes = () =>
|
||||
page.evaluate(`(function(){
|
||||
${CONTRACT_SRC}
|
||||
@@ -1875,29 +1864,20 @@ async function main() {
|
||||
const cBeforeViewshed = await readCounters();
|
||||
await page.evaluate(`(function(){
|
||||
${CONTRACT_SRC}
|
||||
const primitives = window.__godsEyeView.viewer.scene.primitives;
|
||||
const meshes = window.__godsEyeView.engine.meshes;
|
||||
const events = [];
|
||||
window.__qaViewshedTrace = events;
|
||||
// A volume is a synchronous Primitive the pick pass skips; which
|
||||
// camera it belongs to is looked up when the trace is read.
|
||||
const isVolume = (primitive) =>
|
||||
primitive &&
|
||||
primitive.allowPicking === false &&
|
||||
primitive.asynchronous === false &&
|
||||
typeof primitive.get !== 'function';
|
||||
for (const method of ['add', 'remove']) {
|
||||
const original = primitives[method].bind(primitives);
|
||||
primitives[method] = (primitive, ...args) => {
|
||||
if (isVolume(primitive))
|
||||
events.push({
|
||||
method,
|
||||
primitive,
|
||||
at: performance.now(),
|
||||
stack: new Error().stack,
|
||||
});
|
||||
return original(primitive, ...args);
|
||||
};
|
||||
}
|
||||
const create = meshes.createMesh.bind(meshes);
|
||||
meshes.createMesh = (options) => {
|
||||
const primitive = create(options);
|
||||
if (options.id.startsWith('cctv-volume-')) {
|
||||
const record = (method) => events.push({ method, primitive, at: performance.now(), stack: new Error().stack });
|
||||
record('add');
|
||||
const destroy = primitive.destroy.bind(primitive);
|
||||
primitive.destroy = () => { record('remove'); destroy(); };
|
||||
}
|
||||
return primitive;
|
||||
};
|
||||
window.__qaViewshedTraceRead = () => {
|
||||
const owners = new Map(
|
||||
viewshedVolumes().map((entry) => [entry.primitive, entry.camera]),
|
||||
|
||||
@@ -47,7 +47,9 @@ const load = () =>
|
||||
try {
|
||||
await page.setViewport({ width: 1440, height: 900 });
|
||||
await page.goto(
|
||||
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
|
||||
// A view in the link: the app starts there instead of flying to a city
|
||||
// after the shot has placed the camera.
|
||||
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0#v=2&lat=30.2672&lon=-97.742&alt=4000&heading=0&pitch=-90&l=`,
|
||||
{ waitUntil: 'domcontentloaded' },
|
||||
);
|
||||
await page.waitForFunction(
|
||||
@@ -105,7 +107,7 @@ try {
|
||||
from: { ...camera, alt: 4100, altitudeReference: 'ellipsoid' },
|
||||
easing: 'linear',
|
||||
},
|
||||
visual: { style: 'normal', mapStack: 'photoreal' },
|
||||
visual: { style: 'normal', mapStack: 'sentinel2' },
|
||||
layers: { traffic: false },
|
||||
dataPackIds: ['data'],
|
||||
interactions: [
|
||||
@@ -162,21 +164,26 @@ try {
|
||||
),
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() => window.__godsEyeView.tileset?.tilesLoaded === true,
|
||||
() => window.__godsEyeView.sceneDirector.engine.surface.tilesLoaded(),
|
||||
{ timeout: 60000 },
|
||||
);
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
const point = await page.evaluate(async () => {
|
||||
const d = window.__godsEyeView.sceneDirector;
|
||||
const entity = d.viewer.entities.values.find(
|
||||
(e) => e.name === 'data / point',
|
||||
);
|
||||
const pos = entity.position.getValue(d.viewer.clock.currentTime);
|
||||
const screen = d.viewer.scene.cartesianToCanvasCoordinates(pos);
|
||||
const rect = d.viewer.scene.canvas.getBoundingClientRect();
|
||||
const { cartesian3 } = await import('/src/geo/index.js');
|
||||
const pos = cartesian3.fromDegrees(-97.742, 30.2672, 600);
|
||||
const screen = d.engine.projector.worldToWindow(pos);
|
||||
const rect = d.engine.host.canvas.getBoundingClientRect();
|
||||
return { x: screen.x + rect.left, y: screen.y + rect.top };
|
||||
});
|
||||
await page.mouse.click(point.x, point.y);
|
||||
// The click's pick answers asynchronously; the focus follows it.
|
||||
await page
|
||||
.waitForFunction(
|
||||
() => document.activeElement?.dataset.directorAction === 'card',
|
||||
{ timeout: 10000 },
|
||||
)
|
||||
.catch(() => {});
|
||||
check(
|
||||
'real globe pick selects the feature and focuses its keyboard action',
|
||||
await page.evaluate(
|
||||
@@ -204,7 +211,8 @@ try {
|
||||
`${output}/report.json`,
|
||||
JSON.stringify(
|
||||
await page.evaluate(() => ({
|
||||
tilesSettled: window.__godsEyeView.tileset?.tilesLoaded === true,
|
||||
tilesSettled:
|
||||
window.__godsEyeView.sceneDirector.engine.surface.tilesLoaded(),
|
||||
})),
|
||||
null,
|
||||
2,
|
||||
|
||||
Reference in new issue
Block a user