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;
|
const { engine } = globe;
|
||||||
navaraViews.set(engine, {
|
navaraViews.set(engine, {
|
||||||
view: globe.view,
|
view: globe.view,
|
||||||
|
alignDaylightToRestoredView: globe.alignDaylightToRestoredView,
|
||||||
MeshDesc: globe.MeshDesc,
|
MeshDesc: globe.MeshDesc,
|
||||||
EffectDesc: globe.EffectDesc,
|
EffectDesc: globe.EffectDesc,
|
||||||
NavaraEffect: globe.NavaraEffect,
|
NavaraEffect: globe.NavaraEffect,
|
||||||
@@ -264,6 +265,10 @@ export function createViewerlessTools({
|
|||||||
defer,
|
defer,
|
||||||
}) {
|
}) {
|
||||||
const { dataManager } = data;
|
const { dataManager } = data;
|
||||||
|
void styleManager.initialRestorePromise?.then(() => {
|
||||||
|
if (!signal.aborted)
|
||||||
|
navaraViews.get(engine)?.alignDaylightToRestoredView?.();
|
||||||
|
});
|
||||||
const recentImagery = dataManager.layers.get('recent-imagery')?.module;
|
const recentImagery = dataManager.layers.get('recent-imagery')?.module;
|
||||||
if (recentImagery) {
|
if (recentImagery) {
|
||||||
const tool = initNavaraImageryBoxTool({
|
const tool = initNavaraImageryBoxTool({
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ export async function createNavaraGlobe({
|
|||||||
let view;
|
let view;
|
||||||
let engine;
|
let engine;
|
||||||
let overlay = null;
|
let overlay = null;
|
||||||
|
let look;
|
||||||
let removeLook = null;
|
let removeLook = null;
|
||||||
let removePostSampling = null;
|
let removePostSampling = null;
|
||||||
try {
|
try {
|
||||||
@@ -123,13 +124,9 @@ export async function createNavaraGlobe({
|
|||||||
});
|
});
|
||||||
// The sun follows the engine clock (one time source for layers and the
|
// The sun follows the engine clock (one time source for layers and the
|
||||||
// look); the look only runs in preRender, after the engine exists.
|
// look); the look only runs in preRender, after the engine exists.
|
||||||
const look = buildNavaraLook(
|
look = buildNavaraLook(view, plugin, plan, () => engine.clock.frameTime(), {
|
||||||
view,
|
forEachLuminanceUniform,
|
||||||
plugin,
|
});
|
||||||
plan,
|
|
||||||
() => engine.clock.frameTime(),
|
|
||||||
{ forEachLuminanceUniform },
|
|
||||||
);
|
|
||||||
// The look's shadowed sun leaked a light per frame (internals.js).
|
// The look's shadowed sun leaked a light per frame (internals.js).
|
||||||
keepCascadedLightIds(view);
|
keepCascadedLightIds(view);
|
||||||
removePostSampling = createPostSampling(view);
|
removePostSampling = createPostSampling(view);
|
||||||
@@ -213,6 +210,11 @@ export async function createNavaraGlobe({
|
|||||||
NavaraEffect,
|
NavaraEffect,
|
||||||
DrapedMesh,
|
DrapedMesh,
|
||||||
setupMaterialForDrape,
|
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),
|
setRenderLoop: (enabled) => setRenderLoopEnabled(view, enabled),
|
||||||
destroy() {
|
destroy() {
|
||||||
if (destroyed) return;
|
if (destroyed) return;
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ export function buildNavaraLook(
|
|||||||
let cloudsShown = true;
|
let cloudsShown = true;
|
||||||
let cloudsSunlit = true;
|
let cloudsSunlit = true;
|
||||||
let landscape = false;
|
let landscape = false;
|
||||||
|
let daylightTime = null;
|
||||||
|
let fillLight;
|
||||||
|
let fill = 0;
|
||||||
const tone = effect('toneMapping');
|
const tone = effect('toneMapping');
|
||||||
let starsShown = true;
|
let starsShown = true;
|
||||||
|
|
||||||
@@ -100,13 +103,35 @@ export function buildNavaraLook(
|
|||||||
let changed = brighten(!landscapeRequested);
|
let changed = brighten(!landscapeRequested);
|
||||||
if (landscapeRequested !== landscape && tone) {
|
if (landscapeRequested !== landscape && tone) {
|
||||||
landscape = landscapeRequested;
|
landscape = landscapeRequested;
|
||||||
|
daylightTime = null;
|
||||||
view.toneMappingExposure = landscape ? 10 : 1;
|
view.toneMappingExposure = landscape ? 10 : 1;
|
||||||
tone.update({ toneMapping: { mode: landscape ? AGX : ACES_FILMIC } });
|
tone.update({ toneMapping: { mode: landscape ? AGX : ACES_FILMIC } });
|
||||||
changed = true;
|
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 atmosphere = view.atmosphere;
|
||||||
const time =
|
// Choose inspection daylight once. Rotating the camera must not rotate
|
||||||
landscape && place ? landscapeSunTime(now(), place.lng) : now();
|
// 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)) {
|
if (atmosphere && sunDateStale(atmosphere.date.getTime(), time)) {
|
||||||
atmosphere.date = new Date(time);
|
atmosphere.date = new Date(time);
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -170,7 +195,13 @@ export function buildNavaraLook(
|
|||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
return { handles, frame };
|
return {
|
||||||
|
handles,
|
||||||
|
frame,
|
||||||
|
resetDaylight() {
|
||||||
|
daylightTime = null;
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const IRRADIANCE_ON = Object.freeze({
|
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 }) =>
|
const tone = view.added.find(({ desc }) =>
|
||||||
Object.hasOwn(desc, 'toneMapping'),
|
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 };
|
const fuji = { lat: 35.2599, lng: 138.6574 };
|
||||||
look.frame(6508, false, 2600, fuji, true);
|
look.frame(6508, false, 2600, fuji, true);
|
||||||
assert.equal(view.toneMappingExposure, 10);
|
assert.equal(view.toneMappingExposure, 10);
|
||||||
|
assert.equal(opacity.value, 1);
|
||||||
assert.equal(tone.updates.at(-1).toneMapping.mode, 7);
|
assert.equal(tone.updates.at(-1).toneMapping.mode, 7);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
Math.abs(view.atmosphere.date.getTime() / 3_600_000 + fuji.lng / 15 - 13) <
|
Math.abs(view.atmosphere.date.getTime() / 3_600_000 + fuji.lng / 15 - 13) <
|
||||||
1e-6,
|
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);
|
look.frame(6508, false, 2600, fuji, false);
|
||||||
|
assert.equal(ambient.updates.at(-1).ambient.intensity, 0);
|
||||||
assert.equal(view.toneMappingExposure, 1);
|
assert.equal(view.toneMappingExposure, 1);
|
||||||
|
assert.equal(opacity.value, 1);
|
||||||
assert.equal(tone.updates.at(-1).toneMapping.mode, 6);
|
assert.equal(tone.updates.at(-1).toneMapping.mode, 6);
|
||||||
assert.equal(view.atmosphere.date.getTime(), 0);
|
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', () => {
|
test('the sun follows the wall clock, a minute at most behind, only on drawn frames', () => {
|
||||||
let now = 1_000_000;
|
let now = 1_000_000;
|
||||||
const view = fakeView(now);
|
const view = fakeView(now);
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ export function resolveNavaraLook(search = '') {
|
|||||||
export const ACES_FILMIC = 6;
|
export const ACES_FILMIC = 6;
|
||||||
export const AGX = 7;
|
export const AGX = 7;
|
||||||
|
|
||||||
// The Sentinel-2 landscape uses early afternoon at the camera longitude,
|
// Pick early afternoon at the starting landscape longitude. The look keeps
|
||||||
// independently of the telemetry clock. Only the displayed sunlight moves.
|
// this timestamp while the user explores; telemetry uses its own clock.
|
||||||
export function landscapeSunTime(now, longitude) {
|
export function landscapeSunTime(now, longitude) {
|
||||||
const hour = 3_600_000;
|
const hour = 3_600_000;
|
||||||
const day = 24 * hour;
|
const day = 24 * hour;
|
||||||
|
|||||||
Reference in new issue
Block a user