mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 12:23:59 +00:00
Unsubscribe removed Navara effects and models from warm view frames
This commit is contained in:
1 parent
14f7b32537
commit
05b646daa3
16 files changed
+90
-19
No files matched your search
@@ -3616,6 +3616,7 @@
|
||||
"src/geo/developerError.js",
|
||||
"src/time/clock.js",
|
||||
"src/globe/navara/overlay.js",
|
||||
"src/globe/navara/frameDescriptor.js",
|
||||
"src/globe/cesium/clock.js",
|
||||
"src/globe/pickDelivery.js",
|
||||
"src/globe/navara/clock.js",
|
||||
|
||||
@@ -273,7 +273,7 @@ export async function createNavaraScene({
|
||||
alignDaylightToRestoredView: globe.alignDaylightToRestoredView,
|
||||
MeshDesc: globe.MeshDesc,
|
||||
NavaraColor: globe.NavaraColor,
|
||||
EffectDesc: globe.EffectDesc,
|
||||
EffectDesc: globe.LayerEffectDesc,
|
||||
NavaraEffect: globe.NavaraEffect,
|
||||
DrapedMesh: globe.DrapedMesh,
|
||||
setupMaterialForDrape: globe.setupMaterialForDrape,
|
||||
@@ -441,7 +441,7 @@ export function createViewerlessTools({
|
||||
if (window.__godsEyeView === debug) delete window.__godsEyeView;
|
||||
});
|
||||
// Voice on the engine (phase 3, Task 9): the W1 tools drive the engine;
|
||||
// the viewer-only ones (director, annotations…) refuse.
|
||||
// the Director uses the shared session; viewer-only tools declare availability.
|
||||
const voiceCommands = initGevVoiceCommands({
|
||||
...voice,
|
||||
annotationResolver: operations.annotationResolver,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Navara auto-binds descriptor.update on creation but does not unregister
|
||||
// that bound function when its handle is deleted. GEV's synchronous effects
|
||||
// and meshes subscribe with their own callback so a parked view can be reused.
|
||||
export function withFrameUpdates(Base) {
|
||||
return class extends Base {
|
||||
#frame = (time) => this.updateFrame(time);
|
||||
|
||||
onCreate() {
|
||||
super.onCreate();
|
||||
this.view.on('preRender', this.#frame);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.view.off('preRender', this.#frame);
|
||||
super.onDestroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
|
||||
test('deleting a descriptor unsubscribes only that renderer from the warm view', () => {
|
||||
const frames = new Set();
|
||||
const view = {
|
||||
on(name, cb) {
|
||||
assert.equal(name, 'preRender');
|
||||
frames.add(cb);
|
||||
},
|
||||
off(name, cb) {
|
||||
assert.equal(name, 'preRender');
|
||||
frames.delete(cb);
|
||||
},
|
||||
};
|
||||
class Base {
|
||||
view = view;
|
||||
onCreate() {}
|
||||
onDestroy() {
|
||||
this.destroyed = true;
|
||||
}
|
||||
}
|
||||
class Layer extends withFrameUpdates(Base) {
|
||||
ticks = [];
|
||||
updateFrame(time) {
|
||||
this.ticks.push(time);
|
||||
}
|
||||
}
|
||||
const a = new Layer(),
|
||||
b = new Layer();
|
||||
a.onCreate();
|
||||
b.onCreate();
|
||||
assert.equal(typeof a.update, 'undefined', 'no duplicate SDK subscription');
|
||||
for (const cb of frames) cb(1);
|
||||
a.onDestroy();
|
||||
for (const cb of frames) cb(2);
|
||||
assert.deepEqual(a.ticks, [1]);
|
||||
assert.deepEqual(b.ticks, [1, 2]);
|
||||
assert.equal(a.destroyed, true);
|
||||
b.onDestroy();
|
||||
assert.equal(frames.size, 0);
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
// the product way (viewOptions.js) and the GlobeEngine on it. Load it with a
|
||||
// dynamic import() so Navara stays out of the Cesium-mode bundle.
|
||||
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import ThreeView, {
|
||||
Color as NavaraColor,
|
||||
MeshDesc,
|
||||
@@ -209,6 +210,7 @@ export async function createNavaraGlobe({
|
||||
NavaraColor,
|
||||
MeshDesc,
|
||||
EffectDesc,
|
||||
LayerEffectDesc: withFrameUpdates(EffectDesc),
|
||||
NavaraEffect,
|
||||
DrapedMesh,
|
||||
setupMaterialForDrape,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// asks for one frame (view.forceUpdate); per-frame work runs only on frames
|
||||
// the view draws anyway (S5), so an idle view stays idle.
|
||||
|
||||
import { withFrameUpdates } from '../frameDescriptor.js';
|
||||
import {
|
||||
MeshDesc,
|
||||
PickableInstancedMeshWrapper,
|
||||
@@ -25,7 +26,7 @@ import { createNavaraOverlay } from '../overlay.js';
|
||||
const DESC_NAME = 'gevMoverSet';
|
||||
const registered = new WeakSet();
|
||||
|
||||
class GevMoverSetDesc extends MeshDesc {
|
||||
class GevMoverSetDesc extends withFrameUpdates(MeshDesc) {
|
||||
constructor(view, ctx, config) {
|
||||
super(view, ctx, config);
|
||||
this.gev = config[DESC_NAME];
|
||||
@@ -59,7 +60,7 @@ class GevMoverSetDesc extends MeshDesc {
|
||||
return group;
|
||||
}
|
||||
|
||||
update(time) {
|
||||
updateFrame(time) {
|
||||
updateMover(this.gev.readFrameCamera, this.ctx, this.gev.mover, time);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,19 @@ test('disposing a sprite atlas drops decoded images and detaches pending load ca
|
||||
const previous = globalThis.Image;
|
||||
let image;
|
||||
globalThis.Image = class {
|
||||
constructor() { image = this; }
|
||||
constructor() {
|
||||
image = this;
|
||||
}
|
||||
};
|
||||
t.after(() => {
|
||||
if (previous) globalThis.Image = previous;
|
||||
else delete globalThis.Image;
|
||||
});
|
||||
const options = normalizeMoverSetOptions({
|
||||
...spriteOptions, sprite: {
|
||||
sizePx: 24, atlas: { url: '/bracket.svg', cells: { a: [0, 0, 1, 1] } },
|
||||
...spriteOptions,
|
||||
sprite: {
|
||||
sizePx: 24,
|
||||
atlas: { url: '/bracket.svg', cells: { a: [0, 0, 1, 1] } },
|
||||
},
|
||||
});
|
||||
for (const loaded of [false, true]) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// FIRMS opts into a separate native-stage overlay before sharpening/AA.
|
||||
// Only that stage owns an encoded-RGBA8 blend target; W1 stays in place.
|
||||
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import { EffectDesc, Pass as NavaraPass } from '@navaramap/three';
|
||||
import { GevOverlayPass } from './overlayPass.js';
|
||||
import { RGBADepthPacking, Scene } from 'three';
|
||||
@@ -20,7 +21,7 @@ export const OVERLAY_EFFECT_KEY = 'gevOverlay';
|
||||
const PRE_SHARPEN_KEY = 'gevPreSharpenOverlay';
|
||||
const registeredViews = new WeakMap();
|
||||
|
||||
class GevOverlayEffectDesc extends EffectDesc {
|
||||
class GevOverlayEffectDesc extends withFrameUpdates(EffectDesc) {
|
||||
static key = OVERLAY_EFFECT_KEY;
|
||||
// Navara takes the first existing anchor, so the latest pass comes first.
|
||||
static insertAfter = ['fxaa', 'smaa', 'gevSharpen', 'toneMapping'];
|
||||
@@ -56,7 +57,7 @@ class GevOverlayEffectDesc extends EffectDesc {
|
||||
|
||||
// Each drawn frame: the scene depth the overlays test against, then each
|
||||
// overlay's own per-frame work (pixel footprint, origin, viewport).
|
||||
update(time) {
|
||||
updateFrame(time) {
|
||||
const mrt = this.find('mrt');
|
||||
this.overlay.uniforms.tGevSceneDepth.value =
|
||||
mrt?.depthBufferPacking === RGBADepthPacking ? mrt.depthBuffer : null;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
// has no public call to merge effects. A disabled pass is skipped: off costs
|
||||
// nothing.
|
||||
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import { Effect as NavaraEffect, EffectDesc } from '@navaramap/three';
|
||||
import { Effect, EffectAttribute } from 'postprocessing';
|
||||
import { RGBADepthPacking, Uniform } from 'three';
|
||||
@@ -86,7 +87,7 @@ class UnsharpMaskEffect extends Effect {
|
||||
}
|
||||
|
||||
/** config.gevSharpen: { amount } (Cesium's `amount` uniform). */
|
||||
export class SharpenEffectDesc extends EffectDesc {
|
||||
export class SharpenEffectDesc extends withFrameUpdates(EffectDesc) {
|
||||
static key = SHARPEN_EFFECT_KEY;
|
||||
static insertAfter = ['toneMapping'];
|
||||
static insertBefore = ['smaa', 'fxaa', 'gevOverlay', 'final'];
|
||||
@@ -105,7 +106,7 @@ export class SharpenEffectDesc extends EffectDesc {
|
||||
|
||||
// Each frame: the scene depth for the sky mask, when Navara packs it as
|
||||
// RGBA (0.2.1 does); otherwise the whole frame is sharpened.
|
||||
update() {
|
||||
updateFrame() {
|
||||
if (!this.effect) return;
|
||||
const mrt = this.find('mrt');
|
||||
const depth =
|
||||
|
||||
@@ -230,7 +230,7 @@ function groundEffectClass(EffectDesc, NavaraEffect) {
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
update() {
|
||||
updateFrame() {
|
||||
const state = this.owner.state,
|
||||
camera = this.view.camera.raw,
|
||||
mrt = this.find('mrt');
|
||||
|
||||
@@ -40,7 +40,7 @@ function createDrapeEffectClass(EffectDesc, NavaraEffect) {
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
|
||||
update() {
|
||||
updateFrame() {
|
||||
const mrt = this.find('mrt');
|
||||
this.owner.globeNormal.value = mrt?.globeNormalBuffer ?? null;
|
||||
this.owner.globeDepth.value = mrt?.globeDepthBuffer ?? null;
|
||||
|
||||
@@ -108,7 +108,7 @@ export function createImageryGround(
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
update() {
|
||||
updateFrame() {
|
||||
if (!this.effect) return;
|
||||
const camera = this.view.camera.raw,
|
||||
u = this.effect.uniforms,
|
||||
|
||||
@@ -244,7 +244,7 @@ function groundEffectClass(EffectDesc, NavaraEffect) {
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
update() {
|
||||
updateFrame() {
|
||||
const state = this.owner.state,
|
||||
camera = this.view.camera.raw;
|
||||
if (!this.effect || !state || !camera) return;
|
||||
|
||||
@@ -232,7 +232,7 @@ function descriptor(EffectDesc, NavaraEffect) {
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
update() {
|
||||
updateFrame() {
|
||||
if (!this.effect) return;
|
||||
const { state, displayT, active, color, pixelRatio } = this.owner;
|
||||
const camera = this.view.camera.raw;
|
||||
|
||||
@@ -118,7 +118,7 @@ function weatherPass(engine, { view, EffectDesc, NavaraEffect }) {
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
update() {
|
||||
updateFrame() {
|
||||
if (!this.effect) return;
|
||||
const camera = this.view.camera.raw,
|
||||
u = this.effect.uniforms;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Only the renderer and its map survive a switch. The outgoing UI, data feeds,
|
||||
// tools and their subscriptions are still disposed with their application.
|
||||
// This cache owns the parked renderer and its map. The layer session owns
|
||||
// feeds, selection, media and the Director across both renderer attachments.
|
||||
export function createRendererCache() {
|
||||
const idle = new Map();
|
||||
let retainKind;
|
||||
|
||||
Reference in new issue
Block a user