mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +00:00
Classify the Navara launch pad zone onto terrain and 3D tiles with Cesium's fill, rim and emission
The zone was a flat disc at ellipsoid height in Navara's opaque scene: it floated over low pads and sank under raised ground or 3D tiles, with hard edges and an unlit rim. A screen-space pass now paints the ground pixels within the radius of the pad from the scene depth, as Cesium's GroundPrimitive classifies them, using one style shared by both engines (LAUNCH_PAD_ZONE_STYLE).
This commit is contained in:
1 parent
f0d38897f4
commit
d8ef366ab1
6 files changed
+317
-132
No files matched your search
@@ -3689,6 +3689,7 @@
|
||||
"src/layers/localAdsb/render/navara.js",
|
||||
"src/layers/vessels/render/navara.js",
|
||||
"src/layers/launches/render/navara.js",
|
||||
"src/layers/launches/render/navaraPadGround.js",
|
||||
"src/layers/satellites/render/navara.js",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/celestialRing/render/cesium.js",
|
||||
|
||||
@@ -88,6 +88,22 @@ export const MISSION_ORBIT_DASHES_PER_GROUP = 100;
|
||||
|
||||
export const LAUNCH_PAD_ZONE_RADIUS_M = 500;
|
||||
|
||||
/**
|
||||
* The pad zone's look on both engines, as a fraction of the radius: a faint
|
||||
* fill, a bright rim between rimStart and rimEnd whose colour gains the
|
||||
* emission, and a soft edge from rimEnd to the radius. The zone is classified
|
||||
* onto the surface under the pad (terrain and 3D tiles), never drawn at a
|
||||
* height.
|
||||
*/
|
||||
export const LAUNCH_PAD_ZONE_STYLE = Object.freeze({
|
||||
color: '#22e6e6',
|
||||
fillAlpha: 0.105,
|
||||
rimAlpha: 0.72,
|
||||
rimStart: 0.952,
|
||||
rimEnd: 0.985,
|
||||
emission: 0.35,
|
||||
});
|
||||
|
||||
export const LAUNCH_PAD_ZONE_MAX_CAMERA_HEIGHT_M = 120000;
|
||||
|
||||
export const LAUNCH_PAD_ZONE_MAX_CAMERA_DISTANCE_M = 180000;
|
||||
|
||||
@@ -5,8 +5,11 @@ import {
|
||||
MISSION_ORBIT_PATTERN_GROUPS,
|
||||
MISSION_ORBIT_DASHES_PER_GROUP,
|
||||
LAUNCH_PAD_ZONE_RADIUS_M,
|
||||
LAUNCH_PAD_ZONE_STYLE,
|
||||
} from '../policy.js';
|
||||
const HOVER_PICK_INTERVAL_MS = 120;
|
||||
/** A GLSL float literal (always with a decimal point). */
|
||||
const glslFloat = (value) => value.toFixed(4);
|
||||
|
||||
/**
|
||||
* Bound hover picking independently of precise click selection. A move inside
|
||||
@@ -209,19 +212,19 @@ export function createLaunchesCesiumRenderer(engine, { viewer } = {}) {
|
||||
fabric: {
|
||||
type: 'GevLaunchPadZone',
|
||||
uniforms: {
|
||||
color: Cesium.Color.fromCssColorString('#22e6e6'),
|
||||
fillAlpha: 0.105,
|
||||
rimAlpha: 0.72,
|
||||
color: Cesium.Color.fromCssColorString(LAUNCH_PAD_ZONE_STYLE.color),
|
||||
fillAlpha: LAUNCH_PAD_ZONE_STYLE.fillAlpha,
|
||||
rimAlpha: LAUNCH_PAD_ZONE_STYLE.rimAlpha,
|
||||
},
|
||||
source: `
|
||||
czm_material czm_getMaterial(czm_materialInput materialInput) {
|
||||
czm_material material = czm_getDefaultMaterial(materialInput);
|
||||
vec2 centered = (materialInput.st - vec2(0.5)) * 2.0;
|
||||
float radius = length(centered);
|
||||
float inside = 1.0 - smoothstep(0.985, 1.0, radius);
|
||||
float rim = smoothstep(0.952, 0.985, radius) * inside;
|
||||
float inside = 1.0 - smoothstep(${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimEnd)}, 1.0, radius);
|
||||
float rim = smoothstep(${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimStart)}, ${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimEnd)}, radius) * inside;
|
||||
material.diffuse = color.rgb;
|
||||
material.emission = color.rgb * rim * 0.35;
|
||||
material.emission = color.rgb * rim * ${glslFloat(LAUNCH_PAD_ZONE_STYLE.emission)};
|
||||
material.alpha = color.a * inside * mix(fillAlpha, rimAlpha, rim);
|
||||
return material;
|
||||
}`,
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
// Mission points, paths and the documented flat pad disc on Navara.
|
||||
// Mission points, paths and the pad zone on Navara.
|
||||
import * as geo from '../../../geo/index.js';
|
||||
import {
|
||||
Group,
|
||||
Mesh,
|
||||
CircleGeometry,
|
||||
RingGeometry,
|
||||
MeshBasicMaterial,
|
||||
DoubleSide,
|
||||
} from 'three';
|
||||
import { createMoverBank } from '../../aircraft/render/navara.js';
|
||||
import { createEngineLayerView } from '../../shared/render/engineView.js';
|
||||
import { registerViewerEngine } from '../../../globe/cesium/engineFromViewer.js';
|
||||
import { selectionDispatcherFor } from '../../../data/selectionDispatcher.js';
|
||||
import { LAUNCH_PAD_ZONE_RADIUS_M } from '../policy.js';
|
||||
import { createPadGround } from './navaraPadGround.js';
|
||||
|
||||
const PAD_KIND = 'gevLaunchPad';
|
||||
const registered = new WeakSet();
|
||||
const read = (v, time) =>
|
||||
typeof v?.getValue === 'function' ? v.getValue(time) : v;
|
||||
const property = (v) =>
|
||||
@@ -23,7 +13,7 @@ const property = (v) =>
|
||||
const rgba = (c) => (c ? [c.red, c.green, c.blue, c.alpha] : [1, 1, 1, 1]);
|
||||
export function createLaunchesNavaraRenderer(
|
||||
engine,
|
||||
{ view: nativeView, MeshDesc } = {},
|
||||
{ view: nativeView, EffectDesc, NavaraEffect } = {},
|
||||
) {
|
||||
let destroyed = false,
|
||||
sequence = 0,
|
||||
@@ -355,113 +345,31 @@ export function createLaunchesNavaraRenderer(
|
||||
return source;
|
||||
};
|
||||
const createPad = (launch) => {
|
||||
const group = new Group();
|
||||
group.matrixAutoUpdate = false;
|
||||
group.frustumCulled = false;
|
||||
const fill = new Mesh(
|
||||
new CircleGeometry(LAUNCH_PAD_ZONE_RADIUS_M, 96),
|
||||
new MeshBasicMaterial({
|
||||
color: 0x22e6e6,
|
||||
transparent: true,
|
||||
opacity: 0.105,
|
||||
side: DoubleSide,
|
||||
depthWrite: false,
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: -1,
|
||||
polygonOffsetUnits: -4,
|
||||
}),
|
||||
);
|
||||
const rim = new Mesh(
|
||||
new RingGeometry(
|
||||
LAUNCH_PAD_ZONE_RADIUS_M * 0.952,
|
||||
LAUNCH_PAD_ZONE_RADIUS_M * 0.985,
|
||||
96,
|
||||
),
|
||||
new MeshBasicMaterial({
|
||||
color: 0x22e6e6,
|
||||
transparent: true,
|
||||
opacity: 0.687,
|
||||
side: DoubleSide,
|
||||
depthWrite: false,
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: -1,
|
||||
polygonOffsetUnits: -4,
|
||||
}),
|
||||
);
|
||||
fill.frustumCulled = false;
|
||||
rim.frustumCulled = false;
|
||||
rim.renderOrder = 1;
|
||||
group.add(fill, rim);
|
||||
let padHeightM = 0;
|
||||
// Classified onto the surface under the pad as Cesium's GroundPrimitive
|
||||
// (render/navaraPadGround.js); nothing is drawn at a height.
|
||||
const ground =
|
||||
nativeView?.registerEffect && EffectDesc && NavaraEffect
|
||||
? createPadGround(nativeView, { EffectDesc, NavaraEffect }, launch)
|
||||
: null;
|
||||
let dead = false,
|
||||
freed = false;
|
||||
const dispose = () => {
|
||||
if (freed) return;
|
||||
freed = true;
|
||||
for (const mesh of [fill, rim]) {
|
||||
mesh.geometry.dispose();
|
||||
mesh.material.dispose();
|
||||
}
|
||||
};
|
||||
const place = (height) => {
|
||||
if (dead) return;
|
||||
padHeightM = Number.isFinite(height) ? height : 0;
|
||||
group.matrix.fromArray(
|
||||
geo.transforms.eastNorthUpToFixedFrame(
|
||||
geo.cartesian3.fromDegrees(
|
||||
launch.lon,
|
||||
launch.lat,
|
||||
Number.isFinite(height) ? height : 0,
|
||||
),
|
||||
geo.ellipsoid.WGS84,
|
||||
),
|
||||
);
|
||||
group.matrixWorldNeedsUpdate = true;
|
||||
engine.render.request('launch-pad');
|
||||
};
|
||||
// W1 parity decision: a flat ellipsoid disc, not terrain classification.
|
||||
// A coarse tile height sampled during the focus flight can be kilometres off.
|
||||
place(0);
|
||||
let handle;
|
||||
if (nativeView) {
|
||||
if (!registered.has(nativeView)) {
|
||||
registered.add(nativeView);
|
||||
nativeView.registerMesh(
|
||||
PAD_KIND,
|
||||
class extends MeshDesc {
|
||||
constructor(view, ctx, config) {
|
||||
super(view, ctx, config);
|
||||
this.pad = config[PAD_KIND];
|
||||
}
|
||||
createMesh() {
|
||||
return this.pad.group;
|
||||
}
|
||||
onDestroy() {
|
||||
this.pad.dispose();
|
||||
super.onDestroy();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
handle = nativeView.addMesh({ [PAD_KIND]: { group, dispose } });
|
||||
}
|
||||
shown = true;
|
||||
engine.render.request('launch-pad');
|
||||
const pad = {
|
||||
get heightM() {
|
||||
return padHeightM;
|
||||
},
|
||||
/** Classified, as on Cesium: the zone has no height of its own. */
|
||||
heightM: null,
|
||||
get show() {
|
||||
return group.visible;
|
||||
return shown;
|
||||
},
|
||||
set show(value) {
|
||||
if (group.visible === Boolean(value)) return;
|
||||
group.visible = Boolean(value);
|
||||
if (shown === Boolean(value)) return;
|
||||
shown = Boolean(value);
|
||||
if (ground) ground.visible = shown;
|
||||
engine.render.request('launch-pad');
|
||||
},
|
||||
destroy() {
|
||||
if (dead) return;
|
||||
dead = true;
|
||||
handle?.delete();
|
||||
dispose();
|
||||
ground?.destroy();
|
||||
primitives.delete(pad);
|
||||
engine.render.request('launch-pad');
|
||||
},
|
||||
|
||||
@@ -3,7 +3,10 @@ import assert from 'node:assert/strict';
|
||||
import * as geo from '../../../geo/index.js';
|
||||
import { createFakeEngine } from '../../../globe/testing/fakeEngine.js';
|
||||
import { WHITE } from '../../shared/color.js';
|
||||
import { PerspectiveCamera, RGBADepthPacking } from 'three';
|
||||
import { createLaunchesNavaraRenderer } from './navara.js';
|
||||
import { PAD_GROUND_KEY, padGroundShader } from './navaraPadGround.js';
|
||||
import { LAUNCH_PAD_ZONE_STYLE } from '../policy.js';
|
||||
|
||||
const position = geo.cartesian3.fromDegrees(0, 0, 1000);
|
||||
test('mission bank growth, callbacks and source visibility write actual mover stores', async () => {
|
||||
@@ -130,41 +133,116 @@ test('mission hover uses engine drillPick and obeys disabled state', () => {
|
||||
engine.destroy();
|
||||
});
|
||||
|
||||
test('flat pad uses a native mesh description, ellipsoid ENU placement and idempotent disposal', () => {
|
||||
test("the pad zone is classified onto the surface with Cesium's fill, rim and emission", () => {
|
||||
const engine = createFakeEngine();
|
||||
engine.fake.script('sampleHeight', 125);
|
||||
let config,
|
||||
removed = 0;
|
||||
const effects = [];
|
||||
let Desc;
|
||||
class EffectDesc {
|
||||
constructor(view, ctx, config) {
|
||||
this.view = view;
|
||||
this.ctx = ctx;
|
||||
this.config = config;
|
||||
}
|
||||
find() {
|
||||
return mrt;
|
||||
}
|
||||
onDestroy() {}
|
||||
}
|
||||
class NavaraEffect {
|
||||
constructor(camera, effect) {
|
||||
this.effect = effect;
|
||||
}
|
||||
}
|
||||
const mrt = {
|
||||
depthBuffer: { isTexture: true },
|
||||
depthBufferPacking: RGBADepthPacking,
|
||||
};
|
||||
const camera = new PerspectiveCamera();
|
||||
camera.position.set(6378137 + 2000, 0, 0);
|
||||
camera.updateMatrixWorld();
|
||||
const view = {
|
||||
registerMesh() {},
|
||||
addMesh(value) {
|
||||
config = value;
|
||||
camera: { raw: camera },
|
||||
registerEffect(key, desc) {
|
||||
assert.equal(key, PAD_GROUND_KEY);
|
||||
assert.ok(desc.insertAfter.includes('toneMapping'));
|
||||
Desc = desc;
|
||||
},
|
||||
addEffect(config) {
|
||||
const desc = new Desc(
|
||||
view,
|
||||
{ getRenderTarget: () => ({ depthTexture: {} }) },
|
||||
config,
|
||||
);
|
||||
desc.createPass();
|
||||
const record = { desc, visible: config.visible, deleted: false };
|
||||
effects.push(record);
|
||||
return {
|
||||
delete() {
|
||||
removed++;
|
||||
update: ({ visible }) => (record.visible = visible),
|
||||
delete: () => {
|
||||
record.deleted = true;
|
||||
desc.onDestroy();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
const renderer = createLaunchesNavaraRenderer(engine, {
|
||||
view,
|
||||
MeshDesc: class {},
|
||||
EffectDesc,
|
||||
NavaraEffect,
|
||||
});
|
||||
const pad = renderer.createPad({ id: 'x', lon: 0, lat: 0 });
|
||||
assert.equal(
|
||||
engine.fake.callsOf('surface.sampleHeight').length,
|
||||
0,
|
||||
'flat pad must not sample a coarse tile',
|
||||
'classified onto the surface: no coarse tile height is sampled',
|
||||
);
|
||||
const group = config.gevLaunchPad.group;
|
||||
assert.equal(group.children.length, 2);
|
||||
assert.equal(group.matrix.elements[12], 6378137);
|
||||
assert.equal(group.children[0].material.polygonOffsetUnits, -4);
|
||||
assert.equal(pad.heightM, null, 'no height of its own, as on Cesium');
|
||||
assert.equal(effects.length, 1);
|
||||
const { desc } = effects[0];
|
||||
desc.updateFrame();
|
||||
const u = desc.effect.uniforms;
|
||||
// The pad 2 km below the camera, the ENU axes of lon 0, lat 0.
|
||||
assert.deepEqual(u.get('uPadOffset').value.toArray(), [-2000, 0, 0]);
|
||||
assert.deepEqual(u.get('uUp').value.toArray(), [1, 0, 0]);
|
||||
assert.deepEqual(
|
||||
u
|
||||
.get('uEast')
|
||||
.value.toArray()
|
||||
.map((v) => Math.round(v) + 0),
|
||||
[0, 1, 0],
|
||||
);
|
||||
assert.deepEqual(
|
||||
u
|
||||
.get('uNorth')
|
||||
.value.toArray()
|
||||
.map((v) => Math.round(v) + 0),
|
||||
[0, 0, 1],
|
||||
);
|
||||
assert.equal(u.get('tAllDepth').value, mrt.depthBuffer);
|
||||
// Cesium's GevLaunchPadZone material, from the same style.
|
||||
const rgb = u
|
||||
.get('uPadColor')
|
||||
.value.toArray()
|
||||
.map((v) => Math.round(v * 255));
|
||||
assert.deepEqual(rgb, [0x22, 0xe6, 0xe6]);
|
||||
for (const part of [
|
||||
`smoothstep(${LAUNCH_PAD_ZONE_STYLE.rimEnd.toFixed(4)},1.0,radius)`,
|
||||
`smoothstep(${LAUNCH_PAD_ZONE_STYLE.rimStart.toFixed(4)},${LAUNCH_PAD_ZONE_STYLE.rimEnd.toFixed(4)},radius)`,
|
||||
`mix(${LAUNCH_PAD_ZONE_STYLE.fillAlpha.toFixed(4)},${LAUNCH_PAD_ZONE_STYLE.rimAlpha.toFixed(4)},rim)`,
|
||||
`(1.0+${LAUNCH_PAD_ZONE_STYLE.emission.toFixed(4)}*rim)`,
|
||||
'/500.0000',
|
||||
'if(depth<surface-2e-7) return;',
|
||||
])
|
||||
assert.ok(padGroundShader.includes(part), part);
|
||||
pad.show = false;
|
||||
assert.equal(group.visible, false);
|
||||
assert.equal(effects[0].visible, false);
|
||||
pad.show = true;
|
||||
assert.equal(effects[0].visible, true);
|
||||
pad.destroy();
|
||||
pad.destroy();
|
||||
assert.equal(removed, 1);
|
||||
assert.equal(effects[0].deleted, true);
|
||||
assert.equal(effects.filter((e) => e.deleted).length, 1);
|
||||
renderer.destroy();
|
||||
engine.destroy();
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
// The launch pad zone on Navara, classified onto the surface as Cesium's
|
||||
// GroundPrimitive does (render/cesium.js): a screen-space pass reads the depth
|
||||
// of each pixel, puts it back in the world and paints the pixels of the
|
||||
// ground (terrain or 3D tiles, not a model in front of them) that lie within
|
||||
// the zone's radius of the pad, with Cesium's fill, rim and emission. Nothing
|
||||
// is drawn at a height, so the zone neither floats over nor sinks under the
|
||||
// ground, and no height is sampled.
|
||||
import { Effect, EffectAttribute } from 'postprocessing';
|
||||
import { Matrix3, Uniform, Vector3 } from 'three';
|
||||
import * as geo from '../../../geo/index.js';
|
||||
import {
|
||||
groundSurfaceUniforms,
|
||||
updateGroundSurface,
|
||||
} from '../../shared/render/navaraGroundSurface.js';
|
||||
import { LAUNCH_PAD_ZONE_RADIUS_M, LAUNCH_PAD_ZONE_STYLE } from '../policy.js';
|
||||
|
||||
export const PAD_GROUND_KEY = 'gevLaunchPadGround';
|
||||
// The surface above or below the pad that the zone still classifies, as
|
||||
// Cesium's classification volume spans the terrain heights there.
|
||||
export const PAD_GROUND_BAND_M = 9000;
|
||||
const registered = new WeakSet();
|
||||
const glslFloat = (value) => Number(value).toFixed(4);
|
||||
/** A #rrggbb colour as display (sRGB) values, as Cesium's material reads it. */
|
||||
const displayRgb = (hex) =>
|
||||
[1, 3, 5].map((at) => parseInt(hex.slice(at, at + 2), 16) / 255);
|
||||
|
||||
export const padGroundShader = /* glsl */ `
|
||||
uniform sampler2D tAllDepth;
|
||||
uniform sampler2D tSurfaceDepth;
|
||||
uniform mat4 uInverseProjection;
|
||||
uniform float uLogDepthFC;
|
||||
uniform mat3 uViewToWorld;
|
||||
uniform vec3 uPadOffset;
|
||||
uniform vec3 uEast;
|
||||
uniform vec3 uNorth;
|
||||
uniform vec3 uUp;
|
||||
uniform vec3 uPadColor;
|
||||
vec3 toDisplay(vec3 c) {
|
||||
return mix(12.92*c,1.055*pow(max(c,vec3(0.0)),vec3(1.0/2.4))-0.055,step(vec3(0.0031308),c));
|
||||
}
|
||||
vec3 toLinear(vec3 c) {
|
||||
return mix(c/12.92,pow(max((c+0.055)/1.055,vec3(0.0)),vec3(2.4)),step(vec3(0.04045),c));
|
||||
}
|
||||
void mainImage(const in vec4 inputColor,const in vec2 uv,out vec4 outputColor) {
|
||||
outputColor=inputColor;
|
||||
float depth=unpackRGBAToDepth(texture2D(tAllDepth,uv));
|
||||
if(depth>=1.0) return;
|
||||
// Only the ground: a mesh or model in front of it keeps its own colour.
|
||||
float surface=texture2D(tSurfaceDepth,uv).r;
|
||||
if(depth<surface-2e-7) return;
|
||||
float distance=exp2(2.0*depth/uLogDepthFC)-1.0;
|
||||
vec4 ray=uInverseProjection*vec4(uv*2.0-1.0,-1.0,1.0);
|
||||
vec3 point=ray.xyz*(distance/-ray.z);
|
||||
// Relative to the pad, from offsets small enough for single precision.
|
||||
vec3 rel=uViewToWorld*point-uPadOffset;
|
||||
if(abs(dot(rel,uUp))>${glslFloat(PAD_GROUND_BAND_M)}) return;
|
||||
float radius=length(vec2(dot(rel,uEast),dot(rel,uNorth)))/${glslFloat(LAUNCH_PAD_ZONE_RADIUS_M)};
|
||||
if(radius>=1.0) return;
|
||||
// Cesium's GevLaunchPadZone material, blended as in its framebuffer.
|
||||
float inside=1.0-smoothstep(${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimEnd)},1.0,radius);
|
||||
float rim=smoothstep(${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimStart)},${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimEnd)},radius)*inside;
|
||||
float alpha=inside*mix(${glslFloat(LAUNCH_PAD_ZONE_STYLE.fillAlpha)},${glslFloat(LAUNCH_PAD_ZONE_STYLE.rimAlpha)},rim);
|
||||
vec3 color=min(uPadColor*(1.0+${glslFloat(LAUNCH_PAD_ZONE_STYLE.emission)}*rim),vec3(1.0));
|
||||
outputColor=vec4(toLinear(mix(toDisplay(inputColor.rgb),color,alpha)),inputColor.a);
|
||||
}
|
||||
`;
|
||||
|
||||
/** The pad's ECEF position and its east, north and up axes. */
|
||||
export function padFrame(launch) {
|
||||
const m = geo.transforms.eastNorthUpToFixedFrame(
|
||||
geo.cartesian3.fromDegrees(launch.lon, launch.lat, 0),
|
||||
geo.ellipsoid.WGS84,
|
||||
);
|
||||
return {
|
||||
origin: [m[12], m[13], m[14]],
|
||||
east: [m[0], m[1], m[2]],
|
||||
north: [m[4], m[5], m[6]],
|
||||
up: [m[8], m[9], m[10]],
|
||||
};
|
||||
}
|
||||
|
||||
function padGroundClass(EffectDesc, NavaraEffect) {
|
||||
return class LaunchPadGroundDesc extends EffectDesc {
|
||||
static key = PAD_GROUND_KEY;
|
||||
static insertAfter = [
|
||||
'gevEarthquakeGround',
|
||||
'gevObservedWeather',
|
||||
'gevNepalEvent',
|
||||
'gevRecentImagery',
|
||||
'gevFirmsGround',
|
||||
'toneMapping',
|
||||
];
|
||||
static insertBefore = [
|
||||
'gevPreSharpenOverlay',
|
||||
'gevSharpen',
|
||||
'smaa',
|
||||
'fxaa',
|
||||
'gevOverlay',
|
||||
'final',
|
||||
];
|
||||
constructor(view, context, config) {
|
||||
super(view, context, config);
|
||||
this.owner = config[PAD_GROUND_KEY].owner;
|
||||
}
|
||||
createPass() {
|
||||
this.effect = new Effect('GevLaunchPadGround', padGroundShader, {
|
||||
attributes: EffectAttribute.CONVOLUTION,
|
||||
uniforms: new Map([
|
||||
...groundSurfaceUniforms(),
|
||||
['uViewToWorld', new Uniform(new Matrix3())],
|
||||
['uPadOffset', new Uniform(new Vector3())],
|
||||
['uEast', new Uniform(new Vector3())],
|
||||
['uNorth', new Uniform(new Vector3())],
|
||||
['uUp', new Uniform(new Vector3())],
|
||||
[
|
||||
'uPadColor',
|
||||
new Uniform(
|
||||
new Vector3(...displayRgb(LAUNCH_PAD_ZONE_STYLE.color)),
|
||||
),
|
||||
],
|
||||
]),
|
||||
});
|
||||
return new NavaraEffect(this.view.camera.raw, this.effect);
|
||||
}
|
||||
updateFrame() {
|
||||
const frame = this.owner.frame,
|
||||
camera = this.view.camera.raw;
|
||||
if (!this.effect || !frame || !camera) return;
|
||||
updateGroundSurface(this.effect, camera, this.ctx, this.find('mrt'));
|
||||
const u = this.effect.uniforms;
|
||||
u.get('uViewToWorld').value.setFromMatrix4(camera.matrixWorld);
|
||||
const e = camera.matrixWorld.elements;
|
||||
u.get('uPadOffset').value.set(
|
||||
frame.origin[0] - e[12],
|
||||
frame.origin[1] - e[13],
|
||||
frame.origin[2] - e[14],
|
||||
);
|
||||
u.get('uEast').value.fromArray(frame.east);
|
||||
u.get('uNorth').value.fromArray(frame.north);
|
||||
u.get('uUp').value.fromArray(frame.up);
|
||||
}
|
||||
onDestroy() {
|
||||
this.effect?.dispose();
|
||||
super.onDestroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show one pad zone on a Navara view; `show` toggles it, `destroy` removes it.
|
||||
* @param {object} view The Navara view.
|
||||
* @param {{ EffectDesc: Function, NavaraEffect: Function }} sdk
|
||||
* @param {{ lon: number, lat: number }} launch
|
||||
*/
|
||||
export function createPadGround(view, { EffectDesc, NavaraEffect }, launch) {
|
||||
if (!registered.has(view)) {
|
||||
view.registerEffect(
|
||||
PAD_GROUND_KEY,
|
||||
padGroundClass(EffectDesc, NavaraEffect),
|
||||
);
|
||||
registered.add(view);
|
||||
}
|
||||
const owner = { frame: padFrame(launch) };
|
||||
let handle = view.addEffect({
|
||||
[PAD_GROUND_KEY]: { owner },
|
||||
visible: true,
|
||||
});
|
||||
return {
|
||||
owner,
|
||||
set visible(value) {
|
||||
handle?.update({ visible: Boolean(value) });
|
||||
},
|
||||
destroy() {
|
||||
handle?.delete();
|
||||
handle = null;
|
||||
owner.frame = null;
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user