mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Anchor Navara inspection sunlight to the restored view
This commit is contained in:
1 parent
0d15e80e28
commit
3a2a53aef9
5 files changed
+80
-12
No files matched your search
@@ -199,6 +199,7 @@ export async function createNavaraScene({
|
||||
const { engine } = globe;
|
||||
navaraViews.set(engine, {
|
||||
view: globe.view,
|
||||
alignDaylightToRestoredView: globe.alignDaylightToRestoredView,
|
||||
MeshDesc: globe.MeshDesc,
|
||||
EffectDesc: globe.EffectDesc,
|
||||
NavaraEffect: globe.NavaraEffect,
|
||||
@@ -264,6 +265,10 @@ export function createViewerlessTools({
|
||||
defer,
|
||||
}) {
|
||||
const { dataManager } = data;
|
||||
void styleManager.initialRestorePromise?.then(() => {
|
||||
if (!signal.aborted)
|
||||
navaraViews.get(engine)?.alignDaylightToRestoredView?.();
|
||||
});
|
||||
const recentImagery = dataManager.layers.get('recent-imagery')?.module;
|
||||
if (recentImagery) {
|
||||
const tool = initNavaraImageryBoxTool({
|
||||
|
||||
@@ -109,6 +109,7 @@ export async function createNavaraGlobe({
|
||||
let view;
|
||||
let engine;
|
||||
let overlay = null;
|
||||
let look;
|
||||
let removeLook = null;
|
||||
let removePostSampling = null;
|
||||
try {
|
||||
@@ -123,13 +124,9 @@ export async function createNavaraGlobe({
|
||||
});
|
||||
// The sun follows the engine clock (one time source for layers and the
|
||||
// look); the look only runs in preRender, after the engine exists.
|
||||
const look = buildNavaraLook(
|
||||
view,
|
||||
plugin,
|
||||
plan,
|
||||
() => engine.clock.frameTime(),
|
||||
{ forEachLuminanceUniform },
|
||||
);
|
||||
look = buildNavaraLook(view, plugin, plan, () => engine.clock.frameTime(), {
|
||||
forEachLuminanceUniform,
|
||||
});
|
||||
// The look's shadowed sun leaked a light per frame (internals.js).
|
||||
keepCascadedLightIds(view);
|
||||
removePostSampling = createPostSampling(view);
|
||||
@@ -213,6 +210,11 @@ export async function createNavaraGlobe({
|
||||
NavaraEffect,
|
||||
DrapedMesh,
|
||||
setupMaterialForDrape,
|
||||
// Startup can fly from the engine's placeholder camera into a shared view.
|
||||
alignDaylightToRestoredView() {
|
||||
look.resetDaylight();
|
||||
engine.render.request('restored-daylight');
|
||||
},
|
||||
setRenderLoop: (enabled) => setRenderLoopEnabled(view, enabled),
|
||||
destroy() {
|
||||
if (destroyed) return;
|
||||
|
||||
@@ -86,6 +86,9 @@ export function buildNavaraLook(
|
||||
let cloudsShown = true;
|
||||
let cloudsSunlit = true;
|
||||
let landscape = false;
|
||||
let daylightTime = null;
|
||||
let fillLight;
|
||||
let fill = 0;
|
||||
const tone = effect('toneMapping');
|
||||
let starsShown = true;
|
||||
|
||||
@@ -100,13 +103,35 @@ export function buildNavaraLook(
|
||||
let changed = brighten(!landscapeRequested);
|
||||
if (landscapeRequested !== landscape && tone) {
|
||||
landscape = landscapeRequested;
|
||||
daylightTime = null;
|
||||
view.toneMappingExposure = landscape ? 10 : 1;
|
||||
tone.update({ toneMapping: { mode: landscape ? AGX : ACES_FILMIC } });
|
||||
changed = true;
|
||||
}
|
||||
// In orbit, retain some of the underlying imagery in the atmospheric
|
||||
// shadow. Fade up with altitude so ground views keep their original light.
|
||||
const nextFill =
|
||||
landscape && Number.isFinite(height)
|
||||
? Math.min(1, Math.max(0, (height - 50_000) / 250_000))
|
||||
: 0;
|
||||
if (nextFill !== fill) {
|
||||
fill = nextFill;
|
||||
if (fillLight) fillLight.update({ ambient: { intensity: fill } });
|
||||
else fillLight = view.addLight({ ambient: { intensity: fill } });
|
||||
changed = true;
|
||||
}
|
||||
const opacity = aerial?.ref?.raw?.rawEffect?.blendMode.opacity;
|
||||
const strength = 1 - 0.2 * fill;
|
||||
if (opacity && opacity.value !== strength) {
|
||||
opacity.value = strength;
|
||||
changed = true;
|
||||
}
|
||||
const atmosphere = view.atmosphere;
|
||||
const time =
|
||||
landscape && place ? landscapeSunTime(now(), place.lng) : now();
|
||||
// Choose inspection daylight once. Rotating the camera must not rotate
|
||||
// the terminator across the geography.
|
||||
if (landscape && place && daylightTime === null)
|
||||
daylightTime = landscapeSunTime(now(), place.lng);
|
||||
const time = daylightTime ?? now();
|
||||
if (atmosphere && sunDateStale(atmosphere.date.getTime(), time)) {
|
||||
atmosphere.date = new Date(time);
|
||||
changed = true;
|
||||
@@ -170,7 +195,13 @@ export function buildNavaraLook(
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
return { handles, frame };
|
||||
return {
|
||||
handles,
|
||||
frame,
|
||||
resetDaylight() {
|
||||
daylightTime = null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const IRRADIANCE_ON = Object.freeze({
|
||||
|
||||
@@ -167,20 +167,50 @@ test('daylight inspection restores live exposure, tone mapping and time when lef
|
||||
const tone = view.added.find(({ desc }) =>
|
||||
Object.hasOwn(desc, 'toneMapping'),
|
||||
);
|
||||
const aerial = view.added.find(({ desc }) =>
|
||||
Object.hasOwn(desc, 'aerialPerspective'),
|
||||
);
|
||||
const opacity = { value: 1 };
|
||||
aerial.ref = { raw: { rawEffect: { blendMode: { opacity } } } };
|
||||
const fuji = { lat: 35.2599, lng: 138.6574 };
|
||||
look.frame(6508, false, 2600, fuji, true);
|
||||
assert.equal(view.toneMappingExposure, 10);
|
||||
assert.equal(opacity.value, 1);
|
||||
assert.equal(tone.updates.at(-1).toneMapping.mode, 7);
|
||||
assert.ok(
|
||||
Math.abs(view.atmosphere.date.getTime() / 3_600_000 + fuji.lng / 15 - 13) <
|
||||
1e-6,
|
||||
);
|
||||
look.frame(750_149, false, 0, fuji, true);
|
||||
assert.ok(opacity.value < 1);
|
||||
const ambient = view.added.find(({ desc }) => Object.hasOwn(desc, 'ambient'));
|
||||
assert.equal(ambient.desc.ambient.intensity, 1);
|
||||
look.frame(6508, false, 2600, fuji, false);
|
||||
assert.equal(ambient.updates.at(-1).ambient.intensity, 0);
|
||||
assert.equal(view.toneMappingExposure, 1);
|
||||
assert.equal(opacity.value, 1);
|
||||
assert.equal(tone.updates.at(-1).toneMapping.mode, 6);
|
||||
assert.equal(view.atmosphere.date.getTime(), 0);
|
||||
});
|
||||
|
||||
test('inspection sunlight stays fixed through orbit, zoom and move end', () => {
|
||||
const view = fakeView(0);
|
||||
const look = warmCinematic(view);
|
||||
const origin = { lat: -45.9847, lng: 67.8795 };
|
||||
const rotated = { lat: -20, lng: -158 };
|
||||
look.frame(50_151_111, false, 0, origin, true);
|
||||
const sunDate = view.atmosphere.date;
|
||||
look.frame(50_151_111, true, 0, rotated, true);
|
||||
look.frame(750_149, false, 0, rotated, true);
|
||||
assert.equal(view.atmosphere.date, sunDate);
|
||||
look.resetDaylight();
|
||||
look.frame(750_149, false, 0, rotated, true);
|
||||
assert.notEqual(view.atmosphere.date.getTime(), sunDate.getTime());
|
||||
const restored = view.atmosphere.date;
|
||||
look.frame(50_151_111, false, 0, origin, true);
|
||||
assert.equal(view.atmosphere.date, restored);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -52,8 +52,8 @@ export function resolveNavaraLook(search = '') {
|
||||
export const ACES_FILMIC = 6;
|
||||
export const AGX = 7;
|
||||
|
||||
// The Sentinel-2 landscape uses early afternoon at the camera longitude,
|
||||
// independently of the telemetry clock. Only the displayed sunlight moves.
|
||||
// Pick early afternoon at the starting landscape longitude. The look keeps
|
||||
// this timestamp while the user explores; telemetry uses its own clock.
|
||||
export function landscapeSunTime(now, longitude) {
|
||||
const hour = 3_600_000;
|
||||
const day = 24 * hour;
|
||||
|
||||
Reference in new issue
Block a user