From e18f12d34cf7373e47bd3cf255686b3499a18567 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Sat, 7 Nov 2026 06:29:33 +0400 Subject: [PATCH] Keep street maps legible on Navara after dusk Navara lights the map tiles with the real sun and sky, and only the landscape maps took the night ambient fill, so an OSM street map turned black at local night where Cesium keeps it lit. Every base map now takes the night fill; the landscape maps keep their orbital fill as well. Daylight is unchanged. This is why the strict acceptance's Navara pixel and first-frame checks over Austin failed in evening runs: navara-hd, parked-release, hidden-switch, context-loss, geo-import and scope saw a black frame, and the soak and failure matrix waited for a first frame that never came. At night with the fix all nine pass. --- docs/CURRENT-STATE.md | 7 ++++--- src/globe/navara/look.js | 7 +++++-- src/globe/navara/look.test.mjs | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index f11006d2c..3491e7056 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -266,9 +266,10 @@ one application, and only the globe-bound parts move to the next engine. tiles from a disk cache and a trap that fails any read of Cesium before it has loaded. Every catalog layer must be asserted on both engines. Point `PUPPETEER_EXECUTABLE_PATH` at an installed Chrome if Puppeteer's pinned - build is missing. With an OSM base map Navara lights the globe by the real - sun, so scenarios over Austin or San Francisco see a dark frame at local - night and fail their pixel checks; run them in local daylight. + build is missing. Navara lights the globe by the real sun; every base map, + street maps included, takes the night fill after dusk, so a scenario over + Austin or San Francisco at local night still sees a legible map, as on + Cesium (`src/globe/navara/look.js`). - **Navara 0.2.1 workaround.** The SDK never frees pick batch ids, and the mover banks rebuild their instanced meshes on each attach, so every switch leaked one id per instance of capacity (about 260 KiB of WebAssembly memory). diff --git a/src/globe/navara/look.js b/src/globe/navara/look.js index 1e7ec9333..1f8229a48 100644 --- a/src/globe/navara/look.js +++ b/src/globe/navara/look.js @@ -124,14 +124,17 @@ export function buildNavaraLook( ? atmosphere.getSunElevation(place) : Number.NaN; // Keep the map legible from orbit and when descending into darkness. - // At low altitude the fill increases from sunset to civil dusk. + // At low altitude the fill increases from sunset to civil dusk. Every + // base map takes the night fill: the tiles are lit by the sun and the + // sky, so a street map turned black after dusk, where Cesium (the + // reference) keeps it legible; the orbital fill stays the landscape's. const orbitalFill = Number.isFinite(height) ? Math.min(1, Math.max(0, (height - 50_000) / 250_000)) : 0; const nightFill = Number.isFinite(elevation) ? Math.min(1, Math.max(0, -elevation / 6)) : 0; - const nextFill = landscape ? Math.max(orbitalFill, nightFill) : 0; + const nextFill = landscape ? Math.max(orbitalFill, nightFill) : nightFill; if (nextFill !== fill) { fill = nextFill; if (fillLight) fillLight.update({ ambient: { intensity: fill } }); diff --git a/src/globe/navara/look.test.mjs b/src/globe/navara/look.test.mjs index 8b7bdb15e..c6f173311 100644 --- a/src/globe/navara/look.test.mjs +++ b/src/globe/navara/look.test.mjs @@ -248,6 +248,29 @@ test('inspection sunlight stays fixed through orbit, zoom and move end', () => { assert.equal(view.atmosphere.date, restored); }); +test('a street map keeps the night fill after dusk, as Cesium keeps it lit', () => { + // OSM over Austin at 12 km on the live sun: not a landscape, so the sun + // is the wall clock's and the map has no orbital fill. + const view = fakeView(0); + const look = warmCinematic(view); + const austin = { lat: 30.27, lng: -97.74 }; + let elevation = 40; + view.atmosphere.getSunElevation = () => elevation; + look.frame(12_000, false, 0, austin, false); + const ambient = () => + view.added.find(({ desc }) => Object.hasOwn(desc, 'ambient')); + assert.equal(ambient(), undefined, 'no fill in daylight'); + elevation = -3; + look.frame(12_000, false, 0, austin, false); + assert.equal(ambient().desc.ambient.intensity, 0.5, 'half at dusk'); + elevation = -40; + look.frame(12_000, false, 0, austin, false); + assert.equal(ambient().updates.at(-1).ambient.intensity, 1, 'full at night'); + elevation = 10; + look.frame(12_000, false, 0, austin, false); + assert.equal(ambient().updates.at(-1).ambient.intensity, 0, 'none by day'); +}); + test('the sun follows the wall clock, a minute at most behind, only on drawn frames', () => { let now = 1_000_000; const view = fakeView(now);