refactor(services): use the globe engine

groundSnap.heightFor and meshFloorSampler.sampleMeshFloorCells take the
globe engine: engine.surface.sampleHeight for the probe,
engine.surface.tilesLoaded (and, for mesh cells, maps.photorealActive)
for readiness, camera.height for the mesh sampler's altitude gate. Callers
that still hold a viewer or scene (the layers until phase 1c) keep the
unchanged Cesium path.
This commit is contained in:
German Khokhlov committed 2026-10-15 18:34:39 +04:00
1 parent c2a0d20f3d
commit be56144d75
4 files changed
+143 -20

No files matched your search

+40
View File
@@ -128,3 +128,43 @@ test('sampler: outside the google-3d regime nothing is sampled at all', () => {
setMeshFloorPreferred(true);
assert.equal(cachedMeshFloor(cell.lat, cell.lon), null);
});
// --- On a globe engine (engine.surface / camera.height / maps.photorealActive)
async function fakeEngineAt(cell, { heightM = 900, tilesLoaded = true, photoreal = true, sample } = {}) {
const { createFakeEngine } = await import('../globe/testing/fakeEngine.js');
const engine = createFakeEngine({
longitude: Cesium.Math.toRadians(cell.lon),
latitude: Cesium.Math.toRadians(cell.lat),
altitude: heightM,
});
if (photoreal) await engine.maps.setPhotorealTiles({ tileset: {} });
engine.fake.script('tilesLoaded', tilesLoaded);
engine.fake.script('sampleHeight', sample);
return engine;
}
test('sampler on a globe engine: samples engine.surface and latches against the prior', async () => {
reset();
const cell = nextSite();
await seedDem(cell, 120.0);
const engine = await fakeEngineAt(cell, { sample: () => 137.0 });
sampleMeshFloorCells(engine, [cell], { viewerLat: cell.lat, viewerLon: cell.lon, excludeObjects: ['own'] });
assert.equal(cachedMeshFloor(cell.lat, cell.lon), 137.0);
const [call] = engine.fake.callsOf('surface.sampleHeight');
assert.ok(Math.abs(Cesium.Math.toDegrees(call.args[0].latitude) - cell.lat) < 1e-9);
assert.deepEqual(call.args[1], ['own']);
});
test('sampler on a globe engine: a high camera, loading tiles or no photoreal tiles sample nothing', async () => {
reset();
const cell = nextSite();
await seedDem(cell, 120.0);
const opts = { viewerLat: cell.lat, viewerLon: cell.lon };
for (const options of [{ heightM: 90_000 }, { tilesLoaded: false }, { photoreal: false }]) {
const engine = await fakeEngineAt(cell, { ...options, sample: () => 138.0 });
sampleMeshFloorCells(engine, [cell], opts);
assert.equal(engine.fake.callsOf('surface.sampleHeight').length, 0, JSON.stringify(options));
}
assert.equal(cachedMeshFloor(cell.lat, cell.lon), null);
});
+13 -8
View File
@@ -47,6 +47,7 @@
*/
import * as geo from '../geo/index.js';
import { isGlobeEngine } from '../globe/contract.js';
/** Taxi threshold: a cached snap answers directly for moves up to this far from
* the sampled spot (m). Past it the value is demoted to a held last-known and a
@@ -170,7 +171,7 @@ export function firstVisibleTileset(primitives) {
/**
* Create a per-layer ground-snap cache.
* @returns {{
* heightFor: (viewer: Cesium.Viewer, icao: string, pos: Cesium.Cartesian3,
* heightFor: (target: object, icao: string, pos: Cesium.Cartesian3,
* getExclusions?: () => Array<unknown>) => number|null,
* forget: (icao: string) => void,
* clear: () => void,
@@ -281,7 +282,9 @@ export function createGroundSnap({ groundFloor }) {
* A contact that HAS resolved once keeps that answer through a resample outage;
* see `heldSnapM`.
*
* @param {Cesium.Viewer} viewer - Live viewer (sampling + tiles-ready check).
* @param {object} target - The globe engine (`engine.surface`: sampleHeight
* and tilesLoaded), or a live Cesium viewer from callers that still hold
* one (scene.sampleHeight and the first visible tileset's tilesLoaded).
* @param {string} icao - Cache key (per-layer icao space).
* @param {Cesium.Cartesian3} pos - Current display position. Cache movement
* is measured after projecting this input to the WGS84 ellipsoid, so a
@@ -292,7 +295,8 @@ export function createGroundSnap({ groundFloor }) {
* otherwise). Only invoked when a sample actually fires.
* @returns {number|null}
*/
function heightFor(viewer, icao, pos, getExclusions) {
function heightFor(target, icao, pos, getExclusions) {
const engine = isGlobeEngine(target) ? target : null;
const surfacePos = geo.ellipsoid.WGS84.scaleToGeodeticSurface(
pos,
scratchSurfacePos,
@@ -344,7 +348,8 @@ export function createGroundSnap({ groundFloor }) {
entries.set(icao, next);
return heldSnapM(next, surfacePos);
};
if (!_tilesReady(viewer)) return miss();
if (!(engine ? engine.surface.tilesLoaded() : _tilesReady(target)))
return miss();
windowCount += 1;
let sampled;
try {
@@ -354,10 +359,10 @@ export function createGroundSnap({ groundFloor }) {
scratchCarto,
);
// sampleHeight throws when unsupported (no depth textures) — that's a miss.
sampled = viewer.scene.sampleHeight(
carto,
getExclusions ? getExclusions() : undefined,
);
const exclusions = getExclusions ? getExclusions() : undefined;
sampled = engine
? engine.surface.sampleHeight(carto, exclusions)
: target.scene.sampleHeight(carto, exclusions);
} catch {
sampled = undefined;
}
+53
View File
@@ -0,0 +1,53 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import * as geo from '../geo/index.js';
import { createFakeEngine } from '../globe/testing/fakeEngine.js';
import { createGroundSnap } from './groundSnap.js';
const groundFloor = (reports = []) => ({
cachedMeshFloor: () => null,
meshFloorPreferred: () => true,
reportValidatedMeshFloorCell: (...args) => reports.push(args),
});
const KAUS = geo.cartesian3.fromDegrees(-97.67, 30.19, 0);
test('on a globe engine a snap samples engine.surface once and caches it', () => {
const reports = [];
const snap = createGroundSnap({ groundFloor: groundFloor(reports) });
const engine = createFakeEngine();
engine.fake.script('sampleHeight', () => 138.5);
let exclusions = 0;
const exclude = () => {
exclusions++;
return ['own-model'];
};
assert.equal(snap.heightFor(engine, 'a1', KAUS, exclude), 138.5);
assert.equal(snap.heightFor(engine, 'a1', KAUS, exclude), 138.5);
const calls = engine.fake.callsOf('surface.sampleHeight');
assert.equal(calls.length, 1, 'one sample, then the cache');
assert.deepEqual(calls[0].args[1], ['own-model']);
assert.equal(exclusions, 1);
assert.ok(
Math.abs(geo.math.toDegrees(calls[0].args[0].latitude) - 30.19) < 1e-9,
);
assert.equal(reports.length, 1, 'the sample feeds the mesh floor');
});
test('on a globe engine loading tiles or a miss return null without a sample cache', () => {
const snap = createGroundSnap({ groundFloor: groundFloor() });
const engine = createFakeEngine();
engine.fake.script('tilesLoaded', false);
engine.fake.script('sampleHeight', () => 10);
assert.equal(snap.heightFor(engine, 'b1', KAUS), null);
assert.equal(engine.fake.callsOf('surface.sampleHeight').length, 0);
engine.fake.script('tilesLoaded', true);
engine.fake.script('sampleHeight', () => undefined);
assert.equal(snap.heightFor(engine, 'c1', KAUS), null);
assert.equal(engine.fake.callsOf('surface.sampleHeight').length, 1);
assert.equal(
snap.heightFor(engine, 'c1', KAUS),
null,
'backoff after a miss',
);
assert.equal(engine.fake.callsOf('surface.sampleHeight').length, 1);
});
+37 -12
View File
@@ -1,4 +1,6 @@
import * as Cesium from 'cesium';
import * as geo from '../geo/index.js';
import { isGlobeEngine } from '../globe/contract.js';
/** Construct an instance-owned meshFloorSampler service with explicit dependencies. */
export function createMeshFloorSampler({
@@ -50,6 +52,7 @@ export function createMeshFloorSampler({
const MAX_CAMERA_HEIGHT_M = 25_000;
/** @type {Cesium.Cartographic} Scratch for probe coordinates. */
const _scratchProbe = new Cesium.Cartographic();
const _scratchEngineProbe = geo.cartographic.create();
// Regime tracking: mesh cells only apply while the photoreal (google-3d)
// stack renders. main.js re-dispatches MapStackController.onChange as this
@@ -95,7 +98,9 @@ export function createMeshFloorSampler({
* layer poll (never per contact, never per frame). Synchronous — Cesium's
* sampleHeight is a CPU-side query against loaded tiles.
*
* @param {Cesium.Scene|undefined} scene - The scene (skipped when absent/torn down).
* @param {object|undefined} target - The globe engine (engine.surface,
* camera.height, maps.photorealActive), or the Cesium scene from callers
* that still hold one (skipped when absent/torn down).
* @param {Array<{lat: number, lon: number}>} points - Contact/waypoint coords.
* @param {object} [options]
* @param {Array<object>} [options.excludeObjects] - Own billboards/models to
@@ -105,21 +110,31 @@ export function createMeshFloorSampler({
* Viewer subpoint (computed once by the caller's poll).
*/
function sampleMeshFloorCells(
scene,
target,
points,
{ excludeObjects = [], viewerLat, viewerLon } = {},
) {
if (!meshFloorPreferred()) return;
if (!scene || typeof scene.sampleHeight !== 'function') return;
const engine = isGlobeEngine(target) ? target : null;
const scene = engine ? null : target;
if (engine ? !engine.surface.supported : !scene) return;
if (!engine && typeof scene.sampleHeight !== 'function') return;
if (!Array.isArray(points) || !points.length) return;
// Round-5 hardening (the CCTV projectionTilesReady lesson, ported late):
// while tiles are MID-STREAM a probe returns coarse-LOD heights — real
// numbers, wildly wrong — and the one-shot latch made them permanent.
// Only sample when the visible Google tileset reports tilesLoaded AND the
// camera is low enough that the streamed LOD near it is fine-grained.
const camH = scene.camera?.positionCartographic?.height;
const camH = engine
? engine.camera.height()
: scene.camera?.positionCartographic?.height;
if (!Number.isFinite(camH) || camH > MAX_CAMERA_HEIGHT_M) return;
if (!_visibleTilesetLoaded(scene)) return;
if (
!(engine
? engine.maps.photorealActive() && engine.surface.tilesLoaded()
: _visibleTilesetLoaded(scene))
)
return;
let sampled = 0;
const attempted = new Set();
for (const p of points) {
@@ -139,13 +154,23 @@ export function createMeshFloorSampler({
}
let height;
try {
const carto = Cesium.Cartographic.fromDegrees(
cell.lon,
cell.lat,
0,
_scratchProbe,
);
height = scene.sampleHeight(carto, excludeObjects);
if (engine) {
const carto = geo.cartographic.fromDegrees(
cell.lon,
cell.lat,
0,
_scratchEngineProbe,
);
height = engine.surface.sampleHeight(carto, excludeObjects);
} else {
const carto = Cesium.Cartographic.fromDegrees(
cell.lon,
cell.lat,
0,
_scratchProbe,
);
height = scene.sampleHeight(carto, excludeObjects);
}
sampled += 1;
} catch {
continue; // scene mid-teardown — try again next poll