mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 13:52:19 +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
+17
-26
@@ -101,7 +101,7 @@ export async function createApplicationTools({
|
||||
// Cesium only for now: it reads Cesium's camera (positionWC, its
|
||||
// running flight).
|
||||
attachScene(next, { defer: deferScene }) {
|
||||
const viewer = cesiumViewerOf(next.engine);
|
||||
const viewer = next.viewer;
|
||||
if (!viewer) return;
|
||||
deferScene(
|
||||
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
|
||||
* 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. */
|
||||
function cesiumToolBackend({ engine, tileset }) {
|
||||
const viewer = cesiumViewerOf(engine);
|
||||
const {
|
||||
governorRequestRender,
|
||||
holdContinuousRender,
|
||||
releaseContinuousRender,
|
||||
} = renderGovernorFor(engine);
|
||||
function cesiumToolBackend({ engine, viewer, tileset }) {
|
||||
const { governorRequestRender } = renderGovernorFor(engine);
|
||||
return {
|
||||
viewer,
|
||||
startupChrome: {},
|
||||
@@ -300,18 +289,6 @@ function cesiumToolBackend({ engine, tileset }) {
|
||||
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,
|
||||
// stop the default render loop outright — a hidden canvas repaints for
|
||||
@@ -418,6 +395,20 @@ async function attachEngineTools(
|
||||
governor.install();
|
||||
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
|
||||
// in the engine's container, its terminus following the camera's height.
|
||||
installScopeMask(engine);
|
||||
|
||||
+41
-60
@@ -1,12 +1,7 @@
|
||||
import * as geo from './geo/index.js';
|
||||
import { createCelestialView } from './celestialRing/view.js';
|
||||
import {
|
||||
cesiumIcrfToFixed,
|
||||
projectCelestialPoint,
|
||||
flyCelestialViewer,
|
||||
} from './globe/cesium/celestial.js';
|
||||
import { cesiumIcrfToFixed } from './globe/cesium/celestial.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';
|
||||
|
||||
// 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
|
||||
* camera, retain the prior stable bearing and fade the marker.
|
||||
*
|
||||
* @param {number} rightComponent - Dot(direction, camera.rightWC).
|
||||
* @param {number} upComponent - Dot(direction, camera.upWC).
|
||||
* @param {number} rightComponent - Dot(direction, camera.right).
|
||||
* @param {number} upComponent - Dot(direction, camera.up).
|
||||
* @param {number} lastAngle - Previous stable angle in radians.
|
||||
* @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.
|
||||
* 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} height - Viewport height in CSS pixels.
|
||||
* @param {object} [scratchCenter] - Reusable Cesium Cartesian2 result.
|
||||
* @param {object} [scratchToCenter] - Reusable Cesium Cartesian3 result.
|
||||
* @param {object} [scratchCenter] - Reusable Cartesian2 result.
|
||||
* @param {object} [scratchToCenter] - Reusable Cartesian3 result.
|
||||
* @param {import('./globe/contract.js').GlobeProjector | null} [projector] -
|
||||
* The globe engine's projector; without one, Cesium's SceneTransforms
|
||||
* (the same projection on a Cesium scene).
|
||||
* @returns {object|null}
|
||||
*/
|
||||
export function projectEarthDiscToViewport(
|
||||
viewer,
|
||||
engine,
|
||||
width,
|
||||
height,
|
||||
scratchCenter = undefined,
|
||||
scratchToCenter = undefined,
|
||||
projector = null,
|
||||
) {
|
||||
const camera = viewer?.camera;
|
||||
const scene = viewer?.scene;
|
||||
if (!camera || !scene || !(width > 0) || !(height > 0)) return null;
|
||||
if (!engine?.camera || !(width > 0) || !(height > 0)) return null;
|
||||
const host = engine.camera.pose ? engine : celestialHost(engine);
|
||||
const camera = host.camera.pose();
|
||||
|
||||
const distance = geo.cartesian3.magnitude(camera.positionWC);
|
||||
const distance = geo.cartesian3.magnitude(camera.position);
|
||||
if (
|
||||
!Number.isFinite(distance) ||
|
||||
distance <= geo.ellipsoid.WGS84.maximumRadius
|
||||
@@ -275,22 +270,19 @@ export function projectEarthDiscToViewport(
|
||||
return null;
|
||||
|
||||
const toCenter = geo.cartesian3.negate(
|
||||
camera.positionWC,
|
||||
camera.position,
|
||||
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
|
||||
? projector.worldToWindow(geo.cartesian3.ZERO, scratchCenter)
|
||||
: projectCelestialPoint(scene, geo.cartesian3.ZERO, scratchCenter);
|
||||
const center = (projector ?? host.projector).worldToWindow(
|
||||
geo.cartesian3.ZERO,
|
||||
scratchCenter,
|
||||
);
|
||||
if (!center || !Number.isFinite(center.x) || !Number.isFinite(center.y))
|
||||
return null;
|
||||
|
||||
const earthRadius = earthDiscScreenRadius(
|
||||
distance,
|
||||
height,
|
||||
camera.frustum?.fovy,
|
||||
);
|
||||
const earthRadius = earthDiscScreenRadius(distance, height, camera.fovy);
|
||||
if (!earthRadius) return null;
|
||||
|
||||
const keyhole = getKeyholeGeometry(width, height);
|
||||
@@ -401,7 +393,7 @@ let rings = 0;
|
||||
/**
|
||||
* 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
|
||||
* 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 {
|
||||
/**
|
||||
@@ -416,13 +408,7 @@ export class CelestialRing {
|
||||
viewer,
|
||||
{ enabled = true, onAutoDisable = null, store, engine = null } = {},
|
||||
) {
|
||||
this._engine = engine ?? (viewer?.kind ? viewer : null);
|
||||
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._engine = engine ?? (viewer?.kind ? viewer : celestialHost(viewer));
|
||||
this.enabled = !!enabled;
|
||||
this.visible = false;
|
||||
this._onAutoDisable =
|
||||
@@ -455,8 +441,8 @@ export class CelestialRing {
|
||||
);
|
||||
|
||||
this._initView(store);
|
||||
this._removePostRender = this.viewer.scene.postRender.addEventListener(() =>
|
||||
this._draw(),
|
||||
this._removePostRender = this._engine.frames.postRender.addEventListener(
|
||||
() => this._draw(),
|
||||
);
|
||||
// Pre-existing staleness fix (perf wave 2 review): the ephemeris was
|
||||
// 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)
|
||||
this._ephemerisDirty = true;
|
||||
if (typeof document !== 'undefined' && document.hidden) return;
|
||||
this._renderGovernor?.governorRequestRender('celestial-ephemeris');
|
||||
this._engine.render.request('celestial-ephemeris');
|
||||
}, 60_000);
|
||||
// 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.
|
||||
@@ -495,7 +481,7 @@ export class CelestialRing {
|
||||
this._view = {
|
||||
id: ++rings,
|
||||
owner,
|
||||
container: this.viewer.container,
|
||||
container: this._engine.host.container,
|
||||
enabled: this.enabled,
|
||||
visible: false,
|
||||
globeVisible: null,
|
||||
@@ -548,8 +534,7 @@ export class CelestialRing {
|
||||
this._moonCtx = ctx;
|
||||
this._moonRenderKey = '';
|
||||
} else return;
|
||||
if (canvas && this.enabled)
|
||||
this._renderGovernor?.governorRequestRender('celestial-ring');
|
||||
if (canvas && this.enabled) this._engine.render.request('celestial-ring');
|
||||
}
|
||||
|
||||
/** 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
|
||||
// timer requests its own. (perf wave 2 fix — owner playtest finding)
|
||||
if (this.enabled !== wasEnabled)
|
||||
this._renderGovernor?.governorRequestRender('celestial-ring');
|
||||
this._engine.render.request('celestial-ring');
|
||||
if (!this.enabled) {
|
||||
this.visible = false;
|
||||
this._clear();
|
||||
@@ -576,7 +561,7 @@ export class CelestialRing {
|
||||
|
||||
/** Whether the current camera already frames the complete globe inside the keyhole. */
|
||||
isGlobeFullyVisible() {
|
||||
const canvas = this.viewer.scene.canvas;
|
||||
const canvas = this._engine.host.canvas;
|
||||
const width = canvas.clientWidth || canvas.width;
|
||||
const height = canvas.clientHeight || canvas.height;
|
||||
if (!(width > 0 && height > 0)) return false;
|
||||
@@ -591,10 +576,10 @@ export class CelestialRing {
|
||||
* @returns {boolean} Whether a valid camera flight was started.
|
||||
*/
|
||||
focusFullGlobe({ duration = 2.4 } = {}) {
|
||||
const canvas = this.viewer.scene.canvas;
|
||||
const canvas = this._engine.host.canvas;
|
||||
const height = canvas.clientHeight || canvas.height;
|
||||
const cartographic = this.viewer.camera.positionCartographic;
|
||||
const fovy = this.viewer.camera.frustum?.fovy;
|
||||
const pose = this._engine.camera.pose();
|
||||
const { cartographic, fovy } = pose;
|
||||
if (
|
||||
!(height > 0) ||
|
||||
!cartographic ||
|
||||
@@ -620,7 +605,7 @@ export class CelestialRing {
|
||||
this._focusInProgress = true;
|
||||
const finishFocus = () => {
|
||||
this._focusInProgress = false;
|
||||
this.viewer.scene.requestRender?.();
|
||||
this._engine.render.request('celestial-ring');
|
||||
};
|
||||
const flight = {
|
||||
destination: geo.cartesian3.fromRadians(
|
||||
@@ -629,7 +614,7 @@ export class CelestialRing {
|
||||
altitude,
|
||||
),
|
||||
orientation: {
|
||||
heading: this.viewer.camera.heading,
|
||||
heading: pose.heading,
|
||||
pitch: -geo.math.PI_OVER_TWO,
|
||||
roll: 0,
|
||||
},
|
||||
@@ -640,9 +625,7 @@ export class CelestialRing {
|
||||
if (!this.isGlobeFullyVisible()) this._autoDisable();
|
||||
},
|
||||
};
|
||||
if (this._engine)
|
||||
this._engine.camera.flyTo({ ...flight, easing: 'cubicInOut' });
|
||||
else flyCelestialViewer(this.viewer, flight);
|
||||
this._engine.camera.flyTo({ ...flight, easing: 'cubicInOut' });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -713,12 +696,11 @@ export class CelestialRing {
|
||||
/** Return the projected Earth disc used for the full-globe gate. */
|
||||
_projectedEarthDisc(width, height) {
|
||||
return projectEarthDiscToViewport(
|
||||
this.viewer,
|
||||
this._engine,
|
||||
width,
|
||||
height,
|
||||
this._screenCenter,
|
||||
this._toCenter,
|
||||
this._projector,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -795,7 +777,7 @@ export class CelestialRing {
|
||||
const now = performance.now();
|
||||
if (now < this._nextDrawAt) return;
|
||||
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 height = canvas.clientHeight || canvas.height;
|
||||
if (!(width > 0 && height > 0)) return;
|
||||
@@ -828,15 +810,15 @@ export class CelestialRing {
|
||||
const time = skyTime();
|
||||
if (!this._updateEphemeris(time)) return;
|
||||
this._present({ ephemerisUpdates: String(this._ephemerisUpdateCount) });
|
||||
const camera = this.viewer.camera;
|
||||
const camera = this._engine.camera.pose();
|
||||
const sunProjection = celestialScreenAngle(
|
||||
geo.cartesian3.dot(this._sunFixed, camera.rightWC),
|
||||
geo.cartesian3.dot(this._sunFixed, camera.upWC),
|
||||
geo.cartesian3.dot(this._sunFixed, camera.right),
|
||||
geo.cartesian3.dot(this._sunFixed, camera.up),
|
||||
this._sunAngle,
|
||||
);
|
||||
const moonProjection = celestialScreenAngle(
|
||||
geo.cartesian3.dot(this._moonFixed, camera.rightWC),
|
||||
geo.cartesian3.dot(this._moonFixed, camera.upWC),
|
||||
geo.cartesian3.dot(this._moonFixed, camera.right),
|
||||
geo.cartesian3.dot(this._moonFixed, camera.up),
|
||||
this._moonAngle,
|
||||
);
|
||||
if (sunProjection.stable) this._sunAngle = sunProjection.angle;
|
||||
@@ -923,6 +905,5 @@ export class CelestialRing {
|
||||
this._store.publish(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', () => {
|
||||
let flight;
|
||||
const engine = {
|
||||
host: { canvas: { clientWidth: 1200, clientHeight: 800 } },
|
||||
render: { request() {} },
|
||||
camera: {
|
||||
flyTo: (options) => {
|
||||
flight = options;
|
||||
},
|
||||
pose: () => ({
|
||||
cartographic: { longitude: 0, latitude: 0 },
|
||||
heading: 0,
|
||||
fovy: Math.PI / 3,
|
||||
}),
|
||||
flyTo: (options) => { flight = options; },
|
||||
},
|
||||
};
|
||||
const ring = Object.assign(Object.create(CelestialRing.prototype), {
|
||||
_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(ring.focusFullGlobe(), true);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { t, msg, onLocaleChange } from '../i18n/index.js';
|
||||
|
||||
const YOUTUBE_HOSTS = new Set([
|
||||
@@ -538,25 +537,12 @@ function ensureProviderConnections(documentRef, provider) {
|
||||
}
|
||||
}
|
||||
|
||||
function anchorOnScreen(viewer, engine, anchor, root) {
|
||||
if ((!viewer?.scene && !engine) || !anchor || !root) return null;
|
||||
const camera = viewer?.camera;
|
||||
if (engine && !engine.projector.horizonVisible(anchor)) return null;
|
||||
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);
|
||||
function anchorOnScreen(engine, anchor, root) {
|
||||
if (!engine || !anchor || !root) return null;
|
||||
if (!engine.projector.horizonVisible(anchor)) return null;
|
||||
const pixel = engine.projector.worldToWindow(anchor, {});
|
||||
if (!pixel) return null;
|
||||
const canvasBounds = (
|
||||
engine?.host.canvas ?? viewer?.canvas
|
||||
)?.getBoundingClientRect?.();
|
||||
const canvasBounds = engine.host.canvas?.getBoundingClientRect?.();
|
||||
const rootBounds = root.getBoundingClientRect?.();
|
||||
if (!canvasBounds || !rootBounds) return null;
|
||||
const x = pixel.x + canvasBounds.left - rootBounds.left;
|
||||
@@ -855,7 +841,7 @@ export function createBhoteKoshiEmbeddedMedia({
|
||||
|
||||
const position = () => {
|
||||
if (!record) return;
|
||||
const projected = anchorOnScreen(viewer, engine, record.anchor, root);
|
||||
const projected = anchorOnScreen(engine, record.anchor, root);
|
||||
record.card.hidden = !projected;
|
||||
if (projected) {
|
||||
placeCard(record.card, record.leader, projected);
|
||||
|
||||
+14
-27
@@ -884,7 +884,7 @@ function openExternal(url) {
|
||||
if (target) target.opener = null;
|
||||
}
|
||||
|
||||
function focusView(viewer, viewpoint, engine) {
|
||||
function focusView(engine, viewpoint) {
|
||||
announceNavigationAuthority('bhote-koshi-event-focus');
|
||||
const target = Cesium.Cartesian3.fromDegrees(
|
||||
viewpoint.lon,
|
||||
@@ -896,32 +896,20 @@ function focusView(viewer, viewpoint, engine) {
|
||||
Cesium.Math.toRadians(viewpoint.pitchDeg),
|
||||
viewpoint.rangeM,
|
||||
);
|
||||
if (engine) {
|
||||
engine.camera.cancelFlight();
|
||||
engine.camera.flyToSphere(
|
||||
{ center: target, radius: 80 },
|
||||
{
|
||||
heading: offset.heading,
|
||||
pitch: offset.pitch,
|
||||
range: offset.range,
|
||||
duration: 2.2,
|
||||
complete: () => {
|
||||
engine.camera.lookAt(target, offset);
|
||||
engine.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
||||
},
|
||||
engine.camera.cancelFlight();
|
||||
engine.camera.flyToSphere(
|
||||
{ center: target, radius: 80 },
|
||||
{
|
||||
heading: offset.heading,
|
||||
pitch: offset.pitch,
|
||||
range: offset.range,
|
||||
duration: 2.2,
|
||||
complete: () => {
|
||||
engine.camera.lookAt(target, offset);
|
||||
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) {
|
||||
@@ -3521,8 +3509,7 @@ export function createBhoteKoshiEventLayer({
|
||||
_embeddedEvidenceIndex = -1;
|
||||
_embeddedMedia?.hide?.();
|
||||
setSplit(0.5);
|
||||
if (cameraForScene())
|
||||
focusView(_viewer, _event.viewpoints.corridor, _engine);
|
||||
if (_engine) focusView(_engine, _event.viewpoints.corridor);
|
||||
}
|
||||
|
||||
// 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