feat(globe): surface height quality

engine.surface.sampleHeightDetailed(place, { exclude, lod }) answers
{ height, source: 'tiles' | 'terrain', tileSizeM? } (src/globe/surfaceDetail.js).
On Navara tileSizeM is the extent of the tile that answered and
lod 'finest' takes the finest loaded tile, drawn or not; Cesium answers
tiles without a size, else the globe's terrain. acceptedSurfaceHeight()
turns an answer into a height or a miss: tiles larger than
MAX_FLOOR_TILE_SIZE_M (4 km) are too coarse, terrain is not the mesh.

The mesh floor sampler and the ground snap are engine-only now (a viewer
or scene reaches its engine through engineFromViewer, which also registers
the scene) and treat a coarse answer as a miss, as they treat undefined.
Measured on Navara at Austin: while the tiles stream, a 27.9 km tile reads
143.2 m where the ground is 122.5 m (a 1.8 km tile).

`exclude` takes the engine's own mover sets (MoverSet.id): Cesium hides a
set's primitive for the synchronous sample (Cesium matches excluded objects
per billboard, not per collection); Navara's ray only meets tiles.

"sampleHeight undefined in the app on Navara" did not reproduce: the
startup Austin sample is 121.9 m (tile 889 m) on Navara and 122.4 m on
Cesium; one ModelMesh class is patched.

S2 QA (scripts/qa-surface-heights.mjs, 20 places at JFK, SFO, AUS, roofs
and streets): Navara within 2 m of Cesium at 19/20 from 800 m and 18/20
from 15 km with lod 'finest' (gates 18 and 15); offsets per region
-0.21 / +0.4 / +0.1 m; the Empire State roof differs by 39 m (spire
geometry). Navara tile sizes at 800 m: 112-314 m.
This commit is contained in:
German Khokhlov committed 2026-10-16 20:51:29 +04:00
1 parent f640890cf6
commit 645f042450
20 files changed
+635 -244

No files matched your search

+41
View File
@@ -84,3 +84,44 @@ test('tilesLoaded follows the first visible tileset, else the shown globe', () =
tileset.tilesLoaded = true;
assert.equal(surface.tilesLoaded(), true);
});
test('sampleHeightDetailed: tiles from sampleHeight, else terrain; no tile size', () => {
const { surface, scene } = setup();
const place = geo.cartographic.fromDegrees(20, 10, 0);
assert.deepEqual(surface.sampleHeightDetailed(place), {
height: 42,
source: 'tiles',
});
scene.sampleHeight = () => undefined;
assert.deepEqual(surface.sampleHeightDetailed(place, { lod: 'finest' }), {
height: 12.5,
source: 'terrain',
});
assert.throws(
() => surface.sampleHeightDetailed(place, { lod: 2 }),
TypeError,
);
});
test('an engine mover set in exclude is hidden for the sample, native objects pass', () => {
const viewer = createFakeViewer();
const collection = { show: true };
const set = { id: 'flights' };
const { surface } = createCesiumSurface(viewer, {
engineExclude: (entry) => (entry === set ? collection : null),
});
const shownDuring = [];
viewer.scene.sampleHeight = (position, excluded) => {
shownDuring.push(collection.show, excluded);
return 30;
};
const native = { primitive: 1 };
const place = geo.cartographic.fromDegrees(20, 10, 0);
assert.equal(surface.sampleHeight(place, [set, native]), 30);
assert.deepEqual(shownDuring, [false, [native]]);
assert.equal(collection.show, true, 'shown again after the sample');
shownDuring.length = 0;
surface.sampleHeightDetailed(place, { exclude: [set] });
assert.deepEqual(shownDuring, [false, []]);
assert.equal(collection.show, true);
});