Keep Navara's 3D Tiles where the tileset puts them

Navara's 3D Tiles source defaults to model.height 1, which moves a
transformed tileset by a metre along Earth-fixed X: on the synthetic set
flat roofs read 0.12 m high and a sloping roof 0.68 m off, and the drawn
meshes move with them. The adapter sets the height to 0 and records the
package behaviour in WORKAROUNDS.md, with a test that loads the real
package. The downward surface ray now takes its direction from the
place's own normal, so it no longer drifts sideways.

qa-tiles-fixture gains --report (heights and drape pixels per engine,
without asserting), starts on Cesium, and checks each drape hidden, shown
and hidden again; qa-surface-heights uses the plan's 0.5 m tolerance under
tiles and names the point that misses.
This commit is contained in:
German Khokhlov committed 2026-11-10 17:16:23 +04:00
1 parent 3c304502a0
commit 00153acbeb
10 files changed
+430 -97

No files matched your search

+1
View File
@@ -46,6 +46,7 @@ is fixed. Neither excluded check is a signal to remove a workaround.
| `src/globe/navara/movers/batchIds.js:11` | Unregistering pickable wrappers does not recycle their global batch IDs. | 0.2.1 | `batch-ids-retained` (browser, confirmed) checks disjoint allocations after unregistering; this measures ID reuse, not WASM byte reclamation. | Remove `createBatchIdPool` and pool wiring in `movers/index.js`. |
| `src/globe/navara/ground/index.js:145` | The original native-vector path lost the first `show: false` while a new layer was being built. Current ground rendering uses its own pass, so the hide retry is already absent. | 0.2.1 (original path) | No canary: `initial-vector-hide-dropped` timed out before a verdict; the shared readiness error does not identify the failed wait. Requires browser diagnosis. | No current hide retry to delete. Do not remove the ground pass: it also implements draping and materials. |
| `src/globe/navara/maps.js:76` | Idle can be reported before a fetched raster tile arrives after a map switch; the map may keep a stale frame. | 0.2.1 | `idle-before-raster-arrives` (browser, confirmed) holds real raster requests across the idle event, then releases local PNG responses. | Remove the `SETTLE_MS` / `SETTLE_EVERY_MS` redraw timer from `changed`. |
| `src/globe/navara/maps.js` (`setPhotorealTiles`) | The default `model.height=1` displaces transformed 3D Tiles by −1 m in ECEF X on the synthetic ENU fixture; both rendered meshes and CPU queries inherit it. Set `model.height: 0` to preserve the tileset transform. | 0.2.1 | `scripts/qa-tiles-fixture.test.mjs` loads the real WASM package: default transform reproduces the measured flat-top +0.118 m / roof −0.676 m errors; the adapter restores all probes and 40 roof points within 1 µm against independent triangle rays. `qa-tiles-fixture --report` prints browser samples without asserting them. | Remove the explicit zero only when the package's default preserves authored tile positions; retain the authored-geometry regression test. |
| `src/globe/navara/maps.js:145` | Removing the final raster layer interrupts terrain loading at the current view. | 0.2.1 | No canary: needs terrain-loading continuity across final-layer removal; the raster idle fixture does not test terrain. | Remove the add-before-delete constraint and hidden base-raster retention under 3D tiles. |
| `src/globe/navara/maps.js:37`, `src/globe/navara/maps.js:209` | Quantized-mesh root URLs without the tile template can panic. | 0.2.1 | No canary: deliberately panicking WASM would invalidate the remaining view checks; no isolated terrain fixture here. | Use the upstream root-URL API instead of manually appending `{z}/{x}/{y}.terrain`. |
| `src/globe/navara/maps.js:65` | No ion imagery path. | 0.2.1 | No canary: explicit unsupported source; requires a real ion imagery endpoint. | Replace the ion-imagery rejection with native source support. |
+2 -1
View File
@@ -299,7 +299,8 @@ export function createNavaraMaps(
'photoreal',
url,
{ type: '3d-tiles', url },
{ type: '3d-tiles', model: { maxSse: PHOTOREAL_MAX_SSE } },
// Navara's default height shifts transformed tiles along ECEF X.
{ type: '3d-tiles', model: { maxSse: PHOTOREAL_MAX_SSE, height: 0 } },
tilesetUrl
? []
: [
+1 -1
View File
@@ -193,7 +193,7 @@ test('setPhotorealTiles shows Google 3D tiles with a browser key and the Google
tiles.desc.source.desc.url,
'https://tile.googleapis.com/v1/3dtiles/root.json?key=browser%20key',
);
assert.deepEqual(tiles.desc.model, { maxSse: 16 });
assert.deepEqual(tiles.desc.model, { maxSse: 16, height: 0 });
// Hidden, not deleted: Navara 0.2.1 stops loading terrain at the current
// view when the last raster layer is deleted.
const [base] = layersOf(view, 'raster');
+7 -4
View File
@@ -2,9 +2,9 @@
// straight down the ellipsoid normal against the 3D-tile meshes Navara shows.
// Where the shown tiles do not reach (outside the frustum), the loaded
// tiles Navara hides answer, the finest first. Tile positions are float32
// relative to the tile centre, which sits in the
// float64 matrixWorld with no rotation, so a ray taken into a mesh's local
// frame gives Earth-fixed metres. Navara frees the CPU arrays after upload;
// in the mesh's local frame; its float64 matrixWorld maps hits back into
// Earth-fixed metres, including any tileset rotation or translation.
// Navara frees the CPU arrays after upload;
// retainTileGeometry (internals.js) keeps positions and indices. Without a
// tile hit, the answer is Navara's terrain height, as Cesium's sampleHeight
// also samples the globe. Navara internals come in through `hooks`, so this
@@ -37,7 +37,10 @@ export function surfaceRay(place) {
RAY_START_HEIGHT,
WGS84,
);
const normal = WGS84.geodeticSurfaceNormal(origin, cartesian3.create());
const normal = WGS84.geodeticSurfaceNormalCartographic(
place,
cartesian3.create(),
);
return {
origin,
direction: cartesian3.negate(normal, cartesian3.create()),
+6
View File
@@ -84,6 +84,12 @@ test('surfaceRay runs down the ellipsoid normal from 12 km', () => {
cartesian3.create(),
);
near(cartographic.fromCartesian(below, WGS84).height, 0, 1e-6, 'ground');
near(
cartesian3.distance(below, cartesian3.fromDegrees(20, 10, 0)),
0,
1e-6,
'the ray keeps longitude and latitude at the surface',
);
assert.equal(heightAlongRay(11950), 50);
assert.equal(heightAlongRay(null), undefined);
});