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);