mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 06:12:18 +00:00
Fly, follow and draw the celestial ring through the contract
The Bhote Koshi event flies with engine.camera instead of the viewer's camera, tools listens to camera.followChanged instead of the viewer's tracked entity, and the celestial ring uses the engine's host, frames, pose and render requests. The embedded story media no longer imports Cesium, which removes one of the three import exceptions.
This commit is contained in:
1 parent
4f654df3c0
commit
6727aaadcd
7 files changed
+121
-154
No files matched your search
@@ -100,13 +100,15 @@ panel carries no Navara or three. There is no blanket exception for layer
|
|||||||
|
|
||||||
The temporary `engineImportExceptions` list in `scripts/package-boundaries.json`
|
The temporary `engineImportExceptions` list in `scripts/package-boundaries.json`
|
||||||
contains exact file/package pairs with reasons. The checker reports the used
|
contains exact file/package pairs with reasons. The checker reports the used
|
||||||
entries and rejects duplicates, missing reasons and unused entries. Its three
|
entries and rejects duplicates, missing reasons and unused entries. Its two
|
||||||
exceptions are the Bhote Koshi event, locator and embedded-media models in
|
exceptions are the Bhote Koshi event and locator models in `src/data/`, which
|
||||||
`src/data/`, which still construct Cesium property records. Their drawing uses
|
still construct Cesium property records. Their drawing uses shared displays.
|
||||||
shared displays. Exceptions do not relax the portable, contract or cross-engine
|
Exceptions do not relax the portable, contract or cross-engine graph checks.
|
||||||
graph checks. Cesium annotation renderers live in `src/globe/cesium/annotations/`;
|
Cesium annotation renderers live in `src/globe/cesium/annotations/`. Wind's
|
||||||
wind backend adapters live in their engine folders. Wind's older injected-SDK
|
Cesium rendering (the particle canvas, its GPU path and the relief shading)
|
||||||
helpers and the remaining annotation migration are separate debt.
|
lives in `src/globe/cesium/`; `src/layers/wind/` keeps the data, model,
|
||||||
|
presentation and engine-neutral inspection. The remaining annotation migration
|
||||||
|
is separate debt.
|
||||||
|
|
||||||
Source-only factories use dedicated `layers/<family>/source` exports where
|
Source-only factories use dedicated `layers/<family>/source` exports where
|
||||||
available, preserving the same factory implementation without loading rendering.
|
available, preserving the same factory implementation without loading rendering.
|
||||||
|
|||||||
+17
-26
@@ -101,7 +101,7 @@ export async function createApplicationTools({
|
|||||||
// Cesium only for now: it reads Cesium's camera (positionWC, its
|
// Cesium only for now: it reads Cesium's camera (positionWC, its
|
||||||
// running flight).
|
// running flight).
|
||||||
attachScene(next, { defer: deferScene }) {
|
attachScene(next, { defer: deferScene }) {
|
||||||
const viewer = cesiumViewerOf(next.engine);
|
const viewer = next.viewer;
|
||||||
if (!viewer) return;
|
if (!viewer) return;
|
||||||
deferScene(
|
deferScene(
|
||||||
installGlobeInViewRecovery(viewer, {
|
installGlobeInViewRecovery(viewer, {
|
||||||
@@ -255,12 +255,6 @@ export async function createApplicationTools({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The Cesium viewer under an engine, or null on an engine without one: the
|
|
||||||
* one place the tools read it.
|
|
||||||
*/
|
|
||||||
const cesiumViewerOf = (engine) => engine?.extensions?.cesium?.viewer ?? null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tool backend of each engine kind (see attachEngineTools). Cesium's
|
* The tool backend of each engine kind (see attachEngineTools). Cesium's
|
||||||
* holds the tools that still drive its viewer, so they stay absent on
|
* holds the tools that still drive its viewer, so they stay absent on
|
||||||
@@ -273,13 +267,8 @@ const TOOL_BACKENDS = Object.freeze({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** The Cesium tool backend: the shared tools and its own on the viewer. */
|
/** The Cesium tool backend: the shared tools and its own on the viewer. */
|
||||||
function cesiumToolBackend({ engine, tileset }) {
|
function cesiumToolBackend({ engine, viewer, tileset }) {
|
||||||
const viewer = cesiumViewerOf(engine);
|
const { governorRequestRender } = renderGovernorFor(engine);
|
||||||
const {
|
|
||||||
governorRequestRender,
|
|
||||||
holdContinuousRender,
|
|
||||||
releaseContinuousRender,
|
|
||||||
} = renderGovernorFor(engine);
|
|
||||||
return {
|
return {
|
||||||
viewer,
|
viewer,
|
||||||
startupChrome: {},
|
startupChrome: {},
|
||||||
@@ -300,18 +289,6 @@ function cesiumToolBackend({ engine, tileset }) {
|
|||||||
requestRender: governorRequestRender,
|
requestRender: governorRequestRender,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// The follow camera recomputes the tracked target's dead-reckon
|
|
||||||
// position every frame — tracking anything is a per-frame animation.
|
|
||||||
// (perf wave 2)
|
|
||||||
const removeTrackingListener =
|
|
||||||
viewer.trackedEntityChanged.addEventListener(() => {
|
|
||||||
if (viewer.trackedEntity) holdContinuousRender('tracked-entity');
|
|
||||||
else releaseContinuousRender('tracked-entity');
|
|
||||||
});
|
|
||||||
defer(() => {
|
|
||||||
removeTrackingListener();
|
|
||||||
releaseContinuousRender('tracked-entity');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
// Hidden-state suspension (perf wave 2): when the window/tab is hidden,
|
// Hidden-state suspension (perf wave 2): when the window/tab is hidden,
|
||||||
// stop the default render loop outright — a hidden canvas repaints for
|
// stop the default render loop outright — a hidden canvas repaints for
|
||||||
@@ -418,6 +395,20 @@ async function attachEngineTools(
|
|||||||
governor.install();
|
governor.install();
|
||||||
backend.attach?.({ data, styleManager, dataManager }, { signal, defer });
|
backend.attach?.({ data, styleManager, dataManager }, { signal, defer });
|
||||||
|
|
||||||
|
// A followed target moves every frame, even while the camera is still.
|
||||||
|
const updateFollow = () => {
|
||||||
|
if (engine.camera.following())
|
||||||
|
governor.holdContinuousRender('tracked-entity');
|
||||||
|
else governor.releaseContinuousRender('tracked-entity');
|
||||||
|
};
|
||||||
|
const removeFollow =
|
||||||
|
engine.camera.followChanged.addEventListener(updateFollow);
|
||||||
|
updateFollow();
|
||||||
|
defer(() => {
|
||||||
|
removeFollow();
|
||||||
|
governor.releaseContinuousRender('tracked-entity');
|
||||||
|
});
|
||||||
|
|
||||||
// Install the explicit scope mask used by the DISPLAY controls: a canvas
|
// Install the explicit scope mask used by the DISPLAY controls: a canvas
|
||||||
// in the engine's container, its terminus following the camera's height.
|
// in the engine's container, its terminus following the camera's height.
|
||||||
installScopeMask(engine);
|
installScopeMask(engine);
|
||||||
|
|||||||
+41
-60
@@ -1,12 +1,7 @@
|
|||||||
import * as geo from './geo/index.js';
|
import * as geo from './geo/index.js';
|
||||||
import { createCelestialView } from './celestialRing/view.js';
|
import { cesiumIcrfToFixed } from './globe/cesium/celestial.js';
|
||||||
import {
|
|
||||||
cesiumIcrfToFixed,
|
|
||||||
projectCelestialPoint,
|
|
||||||
flyCelestialViewer,
|
|
||||||
} from './globe/cesium/celestial.js';
|
|
||||||
import { createCelestialEphemeris } from './celestialRing/ephemeris.js';
|
import { createCelestialEphemeris } from './celestialRing/ephemeris.js';
|
||||||
import { renderGovernorFor } from './renderGovernor.js';
|
import { celestialHost } from './globe/cesium/celestialHost.js';
|
||||||
import { worldClock, worldTimeFor } from './time/worldClock.js';
|
import { worldClock, worldTimeFor } from './time/worldClock.js';
|
||||||
|
|
||||||
// The ring shows the sun and moon at the time the sky is drawn.
|
// The ring shows the sun and moon at the time the sky is drawn.
|
||||||
@@ -154,8 +149,8 @@ export function isCelestialRingStyleSupported(styleName) {
|
|||||||
* is inverted. When the celestial vector points almost directly into/out of the
|
* is inverted. When the celestial vector points almost directly into/out of the
|
||||||
* camera, retain the prior stable bearing and fade the marker.
|
* camera, retain the prior stable bearing and fade the marker.
|
||||||
*
|
*
|
||||||
* @param {number} rightComponent - Dot(direction, camera.rightWC).
|
* @param {number} rightComponent - Dot(direction, camera.right).
|
||||||
* @param {number} upComponent - Dot(direction, camera.upWC).
|
* @param {number} upComponent - Dot(direction, camera.up).
|
||||||
* @param {number} lastAngle - Previous stable angle in radians.
|
* @param {number} lastAngle - Previous stable angle in radians.
|
||||||
* @returns {{angle:number, opacity:number, stable:boolean}}
|
* @returns {{angle:number, opacity:number, stable:boolean}}
|
||||||
*/
|
*/
|
||||||
@@ -245,29 +240,29 @@ export function earthDiscScreenRadius(cameraDistance, viewportHeight, fovy) {
|
|||||||
* Project the visible Earth disc into viewport coordinates.
|
* Project the visible Earth disc into viewport coordinates.
|
||||||
* Shared by full-globe UI treatments that must agree on visual containment.
|
* Shared by full-globe UI treatments that must agree on visual containment.
|
||||||
*
|
*
|
||||||
* @param {object} viewer - Cesium Viewer-like object.
|
* @param {object} engine - Globe engine or legacy scene host.
|
||||||
* @param {number} width - Viewport width in CSS pixels.
|
* @param {number} width - Viewport width in CSS pixels.
|
||||||
* @param {number} height - Viewport height in CSS pixels.
|
* @param {number} height - Viewport height in CSS pixels.
|
||||||
* @param {object} [scratchCenter] - Reusable Cesium Cartesian2 result.
|
* @param {object} [scratchCenter] - Reusable Cartesian2 result.
|
||||||
* @param {object} [scratchToCenter] - Reusable Cesium Cartesian3 result.
|
* @param {object} [scratchToCenter] - Reusable Cartesian3 result.
|
||||||
* @param {import('./globe/contract.js').GlobeProjector | null} [projector] -
|
* @param {import('./globe/contract.js').GlobeProjector | null} [projector] -
|
||||||
* The globe engine's projector; without one, Cesium's SceneTransforms
|
* The globe engine's projector; without one, Cesium's SceneTransforms
|
||||||
* (the same projection on a Cesium scene).
|
* (the same projection on a Cesium scene).
|
||||||
* @returns {object|null}
|
* @returns {object|null}
|
||||||
*/
|
*/
|
||||||
export function projectEarthDiscToViewport(
|
export function projectEarthDiscToViewport(
|
||||||
viewer,
|
engine,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
scratchCenter = undefined,
|
scratchCenter = undefined,
|
||||||
scratchToCenter = undefined,
|
scratchToCenter = undefined,
|
||||||
projector = null,
|
projector = null,
|
||||||
) {
|
) {
|
||||||
const camera = viewer?.camera;
|
if (!engine?.camera || !(width > 0) || !(height > 0)) return null;
|
||||||
const scene = viewer?.scene;
|
const host = engine.camera.pose ? engine : celestialHost(engine);
|
||||||
if (!camera || !scene || !(width > 0) || !(height > 0)) return null;
|
const camera = host.camera.pose();
|
||||||
|
|
||||||
const distance = geo.cartesian3.magnitude(camera.positionWC);
|
const distance = geo.cartesian3.magnitude(camera.position);
|
||||||
if (
|
if (
|
||||||
!Number.isFinite(distance) ||
|
!Number.isFinite(distance) ||
|
||||||
distance <= geo.ellipsoid.WGS84.maximumRadius
|
distance <= geo.ellipsoid.WGS84.maximumRadius
|
||||||
@@ -275,22 +270,19 @@ export function projectEarthDiscToViewport(
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
const toCenter = geo.cartesian3.negate(
|
const toCenter = geo.cartesian3.negate(
|
||||||
camera.positionWC,
|
camera.position,
|
||||||
scratchToCenter || geo.cartesian3.create(),
|
scratchToCenter || geo.cartesian3.create(),
|
||||||
);
|
);
|
||||||
if (geo.cartesian3.dot(camera.directionWC, toCenter) <= 0) return null;
|
if (geo.cartesian3.dot(camera.direction, toCenter) <= 0) return null;
|
||||||
|
|
||||||
const center = projector
|
const center = (projector ?? host.projector).worldToWindow(
|
||||||
? projector.worldToWindow(geo.cartesian3.ZERO, scratchCenter)
|
geo.cartesian3.ZERO,
|
||||||
: projectCelestialPoint(scene, geo.cartesian3.ZERO, scratchCenter);
|
scratchCenter,
|
||||||
|
);
|
||||||
if (!center || !Number.isFinite(center.x) || !Number.isFinite(center.y))
|
if (!center || !Number.isFinite(center.x) || !Number.isFinite(center.y))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const earthRadius = earthDiscScreenRadius(
|
const earthRadius = earthDiscScreenRadius(distance, height, camera.fovy);
|
||||||
distance,
|
|
||||||
height,
|
|
||||||
camera.frustum?.fovy,
|
|
||||||
);
|
|
||||||
if (!earthRadius) return null;
|
if (!earthRadius) return null;
|
||||||
|
|
||||||
const keyhole = getKeyholeGeometry(width, height);
|
const keyhole = getKeyholeGeometry(width, height);
|
||||||
@@ -401,7 +393,7 @@ let rings = 0;
|
|||||||
/**
|
/**
|
||||||
* Screen-space ring and celestial direction overlay for the full-globe view.
|
* Screen-space ring and celestial direction overlay for the full-globe view.
|
||||||
* The overlay is presented to the celestial ring store, whose view renders it
|
* The overlay is presented to the celestial ring store, whose view renders it
|
||||||
* inside the viewer's container and hands back the two canvases this paints.
|
* inside the engine's container and hands back the two canvases this paints.
|
||||||
*/
|
*/
|
||||||
export class CelestialRing {
|
export class CelestialRing {
|
||||||
/**
|
/**
|
||||||
@@ -416,13 +408,7 @@ export class CelestialRing {
|
|||||||
viewer,
|
viewer,
|
||||||
{ enabled = true, onAutoDisable = null, store, engine = null } = {},
|
{ enabled = true, onAutoDisable = null, store, engine = null } = {},
|
||||||
) {
|
) {
|
||||||
this._engine = engine ?? (viewer?.kind ? viewer : null);
|
this._engine = engine ?? (viewer?.kind ? viewer : celestialHost(viewer));
|
||||||
this._engineView = viewer?.kind ? createCelestialView(viewer) : null;
|
|
||||||
this.viewer = this._engineView?.view ?? viewer;
|
|
||||||
this._projector = engine?.projector ?? null;
|
|
||||||
this._renderGovernor = renderGovernorFor(
|
|
||||||
engine ?? (viewer?.kind ? viewer : null),
|
|
||||||
);
|
|
||||||
this.enabled = !!enabled;
|
this.enabled = !!enabled;
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this._onAutoDisable =
|
this._onAutoDisable =
|
||||||
@@ -455,8 +441,8 @@ export class CelestialRing {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this._initView(store);
|
this._initView(store);
|
||||||
this._removePostRender = this.viewer.scene.postRender.addEventListener(() =>
|
this._removePostRender = this._engine.frames.postRender.addEventListener(
|
||||||
this._draw(),
|
() => this._draw(),
|
||||||
);
|
);
|
||||||
// Pre-existing staleness fix (perf wave 2 review): the ephemeris was
|
// Pre-existing staleness fix (perf wave 2 review): the ephemeris was
|
||||||
// sampled once per visible-enable from the FROZEN app clock, so the
|
// sampled once per visible-enable from the FROZEN app clock, so the
|
||||||
@@ -470,7 +456,7 @@ export class CelestialRing {
|
|||||||
// dirty flag up immediately. (review finding)
|
// dirty flag up immediately. (review finding)
|
||||||
this._ephemerisDirty = true;
|
this._ephemerisDirty = true;
|
||||||
if (typeof document !== 'undefined' && document.hidden) return;
|
if (typeof document !== 'undefined' && document.hidden) return;
|
||||||
this._renderGovernor?.governorRequestRender('celestial-ephemeris');
|
this._engine.render.request('celestial-ephemeris');
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
// A jump in the world time moves the sun and moon at once.
|
// A jump in the world time moves the sun and moon at once.
|
||||||
// Played through, the sky moves on each minute of world time it passes.
|
// Played through, the sky moves on each minute of world time it passes.
|
||||||
@@ -495,7 +481,7 @@ export class CelestialRing {
|
|||||||
this._view = {
|
this._view = {
|
||||||
id: ++rings,
|
id: ++rings,
|
||||||
owner,
|
owner,
|
||||||
container: this.viewer.container,
|
container: this._engine.host.container,
|
||||||
enabled: this.enabled,
|
enabled: this.enabled,
|
||||||
visible: false,
|
visible: false,
|
||||||
globeVisible: null,
|
globeVisible: null,
|
||||||
@@ -548,8 +534,7 @@ export class CelestialRing {
|
|||||||
this._moonCtx = ctx;
|
this._moonCtx = ctx;
|
||||||
this._moonRenderKey = '';
|
this._moonRenderKey = '';
|
||||||
} else return;
|
} else return;
|
||||||
if (canvas && this.enabled)
|
if (canvas && this.enabled) this._engine.render.request('celestial-ring');
|
||||||
this._renderGovernor?.governorRequestRender('celestial-ring');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Enable or disable the user preference for the effect. */
|
/** Enable or disable the user preference for the effect. */
|
||||||
@@ -563,7 +548,7 @@ export class CelestialRing {
|
|||||||
// all. Camera motion covers every later repaint; the 60 s ephemeris
|
// all. Camera motion covers every later repaint; the 60 s ephemeris
|
||||||
// timer requests its own. (perf wave 2 fix — owner playtest finding)
|
// timer requests its own. (perf wave 2 fix — owner playtest finding)
|
||||||
if (this.enabled !== wasEnabled)
|
if (this.enabled !== wasEnabled)
|
||||||
this._renderGovernor?.governorRequestRender('celestial-ring');
|
this._engine.render.request('celestial-ring');
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this._clear();
|
this._clear();
|
||||||
@@ -576,7 +561,7 @@ export class CelestialRing {
|
|||||||
|
|
||||||
/** Whether the current camera already frames the complete globe inside the keyhole. */
|
/** Whether the current camera already frames the complete globe inside the keyhole. */
|
||||||
isGlobeFullyVisible() {
|
isGlobeFullyVisible() {
|
||||||
const canvas = this.viewer.scene.canvas;
|
const canvas = this._engine.host.canvas;
|
||||||
const width = canvas.clientWidth || canvas.width;
|
const width = canvas.clientWidth || canvas.width;
|
||||||
const height = canvas.clientHeight || canvas.height;
|
const height = canvas.clientHeight || canvas.height;
|
||||||
if (!(width > 0 && height > 0)) return false;
|
if (!(width > 0 && height > 0)) return false;
|
||||||
@@ -591,10 +576,10 @@ export class CelestialRing {
|
|||||||
* @returns {boolean} Whether a valid camera flight was started.
|
* @returns {boolean} Whether a valid camera flight was started.
|
||||||
*/
|
*/
|
||||||
focusFullGlobe({ duration = 2.4 } = {}) {
|
focusFullGlobe({ duration = 2.4 } = {}) {
|
||||||
const canvas = this.viewer.scene.canvas;
|
const canvas = this._engine.host.canvas;
|
||||||
const height = canvas.clientHeight || canvas.height;
|
const height = canvas.clientHeight || canvas.height;
|
||||||
const cartographic = this.viewer.camera.positionCartographic;
|
const pose = this._engine.camera.pose();
|
||||||
const fovy = this.viewer.camera.frustum?.fovy;
|
const { cartographic, fovy } = pose;
|
||||||
if (
|
if (
|
||||||
!(height > 0) ||
|
!(height > 0) ||
|
||||||
!cartographic ||
|
!cartographic ||
|
||||||
@@ -620,7 +605,7 @@ export class CelestialRing {
|
|||||||
this._focusInProgress = true;
|
this._focusInProgress = true;
|
||||||
const finishFocus = () => {
|
const finishFocus = () => {
|
||||||
this._focusInProgress = false;
|
this._focusInProgress = false;
|
||||||
this.viewer.scene.requestRender?.();
|
this._engine.render.request('celestial-ring');
|
||||||
};
|
};
|
||||||
const flight = {
|
const flight = {
|
||||||
destination: geo.cartesian3.fromRadians(
|
destination: geo.cartesian3.fromRadians(
|
||||||
@@ -629,7 +614,7 @@ export class CelestialRing {
|
|||||||
altitude,
|
altitude,
|
||||||
),
|
),
|
||||||
orientation: {
|
orientation: {
|
||||||
heading: this.viewer.camera.heading,
|
heading: pose.heading,
|
||||||
pitch: -geo.math.PI_OVER_TWO,
|
pitch: -geo.math.PI_OVER_TWO,
|
||||||
roll: 0,
|
roll: 0,
|
||||||
},
|
},
|
||||||
@@ -640,9 +625,7 @@ export class CelestialRing {
|
|||||||
if (!this.isGlobeFullyVisible()) this._autoDisable();
|
if (!this.isGlobeFullyVisible()) this._autoDisable();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (this._engine)
|
this._engine.camera.flyTo({ ...flight, easing: 'cubicInOut' });
|
||||||
this._engine.camera.flyTo({ ...flight, easing: 'cubicInOut' });
|
|
||||||
else flyCelestialViewer(this.viewer, flight);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,12 +696,11 @@ export class CelestialRing {
|
|||||||
/** Return the projected Earth disc used for the full-globe gate. */
|
/** Return the projected Earth disc used for the full-globe gate. */
|
||||||
_projectedEarthDisc(width, height) {
|
_projectedEarthDisc(width, height) {
|
||||||
return projectEarthDiscToViewport(
|
return projectEarthDiscToViewport(
|
||||||
this.viewer,
|
this._engine,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
this._screenCenter,
|
this._screenCenter,
|
||||||
this._toCenter,
|
this._toCenter,
|
||||||
this._projector,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,7 +777,7 @@ export class CelestialRing {
|
|||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
if (now < this._nextDrawAt) return;
|
if (now < this._nextDrawAt) return;
|
||||||
this._nextDrawAt = now + 1000 / CELESTIAL_MAX_FRAME_RATE;
|
this._nextDrawAt = now + 1000 / CELESTIAL_MAX_FRAME_RATE;
|
||||||
const canvas = this.viewer.scene.canvas;
|
const canvas = this._engine.host.canvas;
|
||||||
const width = canvas.clientWidth || canvas.width;
|
const width = canvas.clientWidth || canvas.width;
|
||||||
const height = canvas.clientHeight || canvas.height;
|
const height = canvas.clientHeight || canvas.height;
|
||||||
if (!(width > 0 && height > 0)) return;
|
if (!(width > 0 && height > 0)) return;
|
||||||
@@ -828,15 +810,15 @@ export class CelestialRing {
|
|||||||
const time = skyTime();
|
const time = skyTime();
|
||||||
if (!this._updateEphemeris(time)) return;
|
if (!this._updateEphemeris(time)) return;
|
||||||
this._present({ ephemerisUpdates: String(this._ephemerisUpdateCount) });
|
this._present({ ephemerisUpdates: String(this._ephemerisUpdateCount) });
|
||||||
const camera = this.viewer.camera;
|
const camera = this._engine.camera.pose();
|
||||||
const sunProjection = celestialScreenAngle(
|
const sunProjection = celestialScreenAngle(
|
||||||
geo.cartesian3.dot(this._sunFixed, camera.rightWC),
|
geo.cartesian3.dot(this._sunFixed, camera.right),
|
||||||
geo.cartesian3.dot(this._sunFixed, camera.upWC),
|
geo.cartesian3.dot(this._sunFixed, camera.up),
|
||||||
this._sunAngle,
|
this._sunAngle,
|
||||||
);
|
);
|
||||||
const moonProjection = celestialScreenAngle(
|
const moonProjection = celestialScreenAngle(
|
||||||
geo.cartesian3.dot(this._moonFixed, camera.rightWC),
|
geo.cartesian3.dot(this._moonFixed, camera.right),
|
||||||
geo.cartesian3.dot(this._moonFixed, camera.upWC),
|
geo.cartesian3.dot(this._moonFixed, camera.up),
|
||||||
this._moonAngle,
|
this._moonAngle,
|
||||||
);
|
);
|
||||||
if (sunProjection.stable) this._sunAngle = sunProjection.angle;
|
if (sunProjection.stable) this._sunAngle = sunProjection.angle;
|
||||||
@@ -923,6 +905,5 @@ export class CelestialRing {
|
|||||||
this._store.publish(null);
|
this._store.publish(null);
|
||||||
}
|
}
|
||||||
this._sunCanvas = this._sunCtx = this._moonCanvas = this._moonCtx = null;
|
this._sunCanvas = this._sunCtx = this._moonCanvas = this._moonCtx = null;
|
||||||
this._engineView?.destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,25 +5,19 @@ import { CelestialRing } from './celestialRing.js';
|
|||||||
test('full globe focus uses the engine without a Cesium global', () => {
|
test('full globe focus uses the engine without a Cesium global', () => {
|
||||||
let flight;
|
let flight;
|
||||||
const engine = {
|
const engine = {
|
||||||
|
host: { canvas: { clientWidth: 1200, clientHeight: 800 } },
|
||||||
|
render: { request() {} },
|
||||||
camera: {
|
camera: {
|
||||||
flyTo: (options) => {
|
pose: () => ({
|
||||||
flight = options;
|
cartographic: { longitude: 0, latitude: 0 },
|
||||||
},
|
heading: 0,
|
||||||
|
fovy: Math.PI / 3,
|
||||||
|
}),
|
||||||
|
flyTo: (options) => { flight = options; },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const ring = Object.assign(Object.create(CelestialRing.prototype), {
|
const ring = Object.assign(Object.create(CelestialRing.prototype), {
|
||||||
_engine: engine,
|
_engine: engine,
|
||||||
viewer: {
|
|
||||||
scene: {
|
|
||||||
canvas: { clientWidth: 1200, clientHeight: 800 },
|
|
||||||
requestRender() {},
|
|
||||||
},
|
|
||||||
camera: {
|
|
||||||
positionCartographic: { longitude: 0, latitude: 0 },
|
|
||||||
heading: 0,
|
|
||||||
frustum: { fovy: Math.PI / 3 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
assert.equal(globalThis.Cesium, undefined);
|
assert.equal(globalThis.Cesium, undefined);
|
||||||
assert.equal(ring.focusFullGlobe(), true);
|
assert.equal(ring.focusFullGlobe(), true);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as Cesium from 'cesium';
|
|
||||||
import { t, msg, onLocaleChange } from '../i18n/index.js';
|
import { t, msg, onLocaleChange } from '../i18n/index.js';
|
||||||
|
|
||||||
const YOUTUBE_HOSTS = new Set([
|
const YOUTUBE_HOSTS = new Set([
|
||||||
@@ -538,25 +537,12 @@ function ensureProviderConnections(documentRef, provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function anchorOnScreen(viewer, engine, anchor, root) {
|
function anchorOnScreen(engine, anchor, root) {
|
||||||
if ((!viewer?.scene && !engine) || !anchor || !root) return null;
|
if (!engine || !anchor || !root) return null;
|
||||||
const camera = viewer?.camera;
|
if (!engine.projector.horizonVisible(anchor)) return null;
|
||||||
if (engine && !engine.projector.horizonVisible(anchor)) return null;
|
const pixel = engine.projector.worldToWindow(anchor, {});
|
||||||
if (camera?.positionWC && camera?.directionWC) {
|
|
||||||
const delta = Cesium.Cartesian3.subtract(
|
|
||||||
anchor,
|
|
||||||
camera.positionWC,
|
|
||||||
new Cesium.Cartesian3(),
|
|
||||||
);
|
|
||||||
if (Cesium.Cartesian3.dot(delta, camera.directionWC) <= 0) return null;
|
|
||||||
}
|
|
||||||
const pixel = engine
|
|
||||||
? engine.projector.worldToWindow(anchor, {})
|
|
||||||
: Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, anchor);
|
|
||||||
if (!pixel) return null;
|
if (!pixel) return null;
|
||||||
const canvasBounds = (
|
const canvasBounds = engine.host.canvas?.getBoundingClientRect?.();
|
||||||
engine?.host.canvas ?? viewer?.canvas
|
|
||||||
)?.getBoundingClientRect?.();
|
|
||||||
const rootBounds = root.getBoundingClientRect?.();
|
const rootBounds = root.getBoundingClientRect?.();
|
||||||
if (!canvasBounds || !rootBounds) return null;
|
if (!canvasBounds || !rootBounds) return null;
|
||||||
const x = pixel.x + canvasBounds.left - rootBounds.left;
|
const x = pixel.x + canvasBounds.left - rootBounds.left;
|
||||||
@@ -855,7 +841,7 @@ export function createBhoteKoshiEmbeddedMedia({
|
|||||||
|
|
||||||
const position = () => {
|
const position = () => {
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
const projected = anchorOnScreen(viewer, engine, record.anchor, root);
|
const projected = anchorOnScreen(engine, record.anchor, root);
|
||||||
record.card.hidden = !projected;
|
record.card.hidden = !projected;
|
||||||
if (projected) {
|
if (projected) {
|
||||||
placeCard(record.card, record.leader, projected);
|
placeCard(record.card, record.leader, projected);
|
||||||
|
|||||||
+14
-27
@@ -884,7 +884,7 @@ function openExternal(url) {
|
|||||||
if (target) target.opener = null;
|
if (target) target.opener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusView(viewer, viewpoint, engine) {
|
function focusView(engine, viewpoint) {
|
||||||
announceNavigationAuthority('bhote-koshi-event-focus');
|
announceNavigationAuthority('bhote-koshi-event-focus');
|
||||||
const target = Cesium.Cartesian3.fromDegrees(
|
const target = Cesium.Cartesian3.fromDegrees(
|
||||||
viewpoint.lon,
|
viewpoint.lon,
|
||||||
@@ -896,32 +896,20 @@ function focusView(viewer, viewpoint, engine) {
|
|||||||
Cesium.Math.toRadians(viewpoint.pitchDeg),
|
Cesium.Math.toRadians(viewpoint.pitchDeg),
|
||||||
viewpoint.rangeM,
|
viewpoint.rangeM,
|
||||||
);
|
);
|
||||||
if (engine) {
|
engine.camera.cancelFlight();
|
||||||
engine.camera.cancelFlight();
|
engine.camera.flyToSphere(
|
||||||
engine.camera.flyToSphere(
|
{ center: target, radius: 80 },
|
||||||
{ center: target, radius: 80 },
|
{
|
||||||
{
|
heading: offset.heading,
|
||||||
heading: offset.heading,
|
pitch: offset.pitch,
|
||||||
pitch: offset.pitch,
|
range: offset.range,
|
||||||
range: offset.range,
|
duration: 2.2,
|
||||||
duration: 2.2,
|
complete: () => {
|
||||||
complete: () => {
|
engine.camera.lookAt(target, offset);
|
||||||
engine.camera.lookAt(target, offset);
|
engine.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
||||||
engine.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
viewer.camera.cancelFlight?.();
|
|
||||||
viewer.camera.flyToBoundingSphere(new Cesium.BoundingSphere(target, 80), {
|
|
||||||
offset,
|
|
||||||
duration: 2.2,
|
|
||||||
complete: () => {
|
|
||||||
viewer.camera.lookAt(target, offset);
|
|
||||||
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setElementProperty(element, property, value) {
|
function setElementProperty(element, property, value) {
|
||||||
@@ -3521,8 +3509,7 @@ export function createBhoteKoshiEventLayer({
|
|||||||
_embeddedEvidenceIndex = -1;
|
_embeddedEvidenceIndex = -1;
|
||||||
_embeddedMedia?.hide?.();
|
_embeddedMedia?.hide?.();
|
||||||
setSplit(0.5);
|
setSplit(0.5);
|
||||||
if (cameraForScene())
|
if (_engine) focusView(_engine, _event.viewpoints.corridor);
|
||||||
focusView(_viewer, _event.viewpoints.corridor, _engine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each attachment and release advances this. An attachment that resumes
|
// Each attachment and release advances this. An attachment that resumes
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Compatibility for callers that still construct the ring on a bare viewer.
|
||||||
|
import { flyCelestialViewer, projectCelestialPoint } from './celestial.js';
|
||||||
|
|
||||||
|
export function celestialHost(viewer) {
|
||||||
|
return {
|
||||||
|
host: { container: viewer.container, canvas: viewer.scene.canvas },
|
||||||
|
frames: { postRender: viewer.scene.postRender },
|
||||||
|
camera: {
|
||||||
|
pose: () => ({
|
||||||
|
position: viewer.camera.positionWC,
|
||||||
|
cartographic: viewer.camera.positionCartographic,
|
||||||
|
direction: viewer.camera.directionWC,
|
||||||
|
up: viewer.camera.upWC,
|
||||||
|
right: viewer.camera.rightWC,
|
||||||
|
heading: viewer.camera.heading,
|
||||||
|
fovy: viewer.camera.frustum?.fovy,
|
||||||
|
}),
|
||||||
|
flyTo: (options) => flyCelestialViewer(viewer, options),
|
||||||
|
},
|
||||||
|
projector: {
|
||||||
|
worldToWindow: (position, result) =>
|
||||||
|
projectCelestialPoint(viewer.scene, position, result),
|
||||||
|
},
|
||||||
|
render: { request: () => viewer.scene.requestRender?.() },
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in new issue
Block a user