Spaces:
Configuration error
Configuration error
| // The one module that names a DOM container and asks for a WebGL context. | |
| // | |
| // Split out of CesiumController's constructor, which used to do this itself | |
| // against a hardcoded element id. Nothing else in the app constructs a Viewer, | |
| // so this is the only file that cannot run outside a browser — and everything | |
| // downstream of it takes the viewer as an argument instead of making one. | |
| import { Tonemapper } from "@cesium/engine"; | |
| import { Viewer } from "@cesium/widgets"; | |
| /** | |
| * A viewer with this app's widget selection and scene defaults already applied. | |
| * | |
| * No `animation` and no `timeline` on any device: the clock deck replaces both | |
| * (`ClockDeck.vue`), and neither widget fits a phone's width. | |
| * | |
| * `minimalUI` is the caller's answer, not this module's, and decides only the | |
| * fullscreen button: nothing to do in an iframe, no meaning on iOS. Read once | |
| * (`DeviceDetect.minimalUI`) rather than re-derived per consumer. | |
| */ | |
| export function createViewer(container: string | Element, options: { minimalUI: boolean }): Viewer { | |
| const { minimalUI } = options; | |
| const viewer = new Viewer(container, { | |
| animation: false, | |
| // No base layer here: the store's layer stack is the only default, and it | |
| // arrives through sceneSync's immediate watcher a tick later. Naming one here | |
| // as well meant two defaults that could drift. | |
| baseLayer: false, | |
| baseLayerPicker: false, | |
| fullscreenButton: !minimalUI, | |
| fullscreenElement: document.body, | |
| geocoder: false, | |
| homeButton: false, | |
| infoBox: false, | |
| navigationHelpButton: false, | |
| navigationInstructionsInitiallyVisible: false, | |
| sceneModePicker: false, | |
| selectionIndicator: false, | |
| timeline: false, | |
| contextOptions: { | |
| webgl: { | |
| alpha: true, | |
| }, | |
| }, | |
| }); | |
| // Cesium's own wording for the lightbox link is "Data attribution", which is 28 px | |
| // of a credit line the clock deck has to share the bottom row with — and the two | |
| // breakpoints that decide where that line goes are measured off its width | |
| // (`useClockDeckChrome`, and the corner rule in main.css). "Attribution" says the | |
| // same thing about the same lightbox. | |
| // | |
| // Written once, because CreditDisplay sets this text in its constructor and never | |
| // rewrites it: only the credit list beside it is rebuilt as credits come and go. | |
| const expandLink = viewer.container.querySelector(".cesium-credit-expand-link"); | |
| if (expandLink) { | |
| expandLink.textContent = "Attribution"; | |
| } | |
| viewer.clock.shouldAnimate = true; | |
| viewer.scene.globe.enableLighting = true; | |
| viewer.scene.highDynamicRange = true; | |
| viewer.scene.maximumRenderTimeChange = 1 / 30; | |
| viewer.scene.requestRenderMode = true; | |
| // Away from Cesium's PBR_NEUTRAL default, because that curve is wrong for a | |
| // scene this dark. | |
| // | |
| // Its black-point term subtracts min(r,g,b) from every channel while that | |
| // minimum is under linear 0.08 (see czm_pbrNeutralTonemapping). On a near-black | |
| // pixel the minimum is nearly the whole signal, so the subtraction strips the | |
| // achromatic part and leaves only the channel imbalance — which czm_inverseGamma | |
| // then stretches. A neutral sRGB grey of (18,17,16) comes out around (9,7,2). | |
| // | |
| // Measured, not assumed. It renders the faint sky as olive mottling at any star | |
| // map resolution (src/config/starMaps.ts), and it is not confined to the sky: | |
| // over Manhattan with OSM Buildings, 37% of the frame sits under luminance 40, | |
| // and there it claims a mean saturation of 0.598 against ACES's 0.260. Shadowed | |
| // building faces get the same treatment as the sky. | |
| // | |
| // ACES costs about 5% luminance and 6% saturation on properly lit surfaces, and | |
| // agrees with PBR_NEUTRAL to within 6% in the 80-140 luminance band, with a mean | |
| // absolute channel delta under 11 everywhere — the globe and the satellite models | |
| // are visually unchanged. Same pass count either way, so no frame-time change. | |
| viewer.scene.postProcessStages.tonemapper = Tonemapper.ACES; | |
| // Otherwise render-on-demand and Cesium's "wait for the data sources" rule | |
| // deadlock each other. | |
| // | |
| // A static geometry primitive is built asynchronously and only advances a | |
| // state per render. While it is unfinished `DataSourceDisplay.update` reports | |
| // false, and by default the Viewer answers that by clearing `clock.canAnimate` | |
| // — so simulation time stops. But `maximumRenderTimeChange` above is what asks | |
| // for the next frame, and it asks based on simulation time having moved. Time | |
| // cannot move until the primitive is ready, the primitive cannot become ready | |
| // without frames, and no frame is requested: the clock stops dead the first | |
| // time a ground-track corridor is switched on and never restarts. | |
| // | |
| // Nothing here needs the guarantee it gives up. Suspending animation exists so | |
| // a CZML clip does not run ahead of geometry still streaming in; every | |
| // geometry in this app is generated locally from a propagator, and a swath | |
| // corridor arriving a frame late is not worth stopping time over. | |
| viewer.allowDataSourcesToSuspendAnimation = false; | |
| return viewer; | |
| } | |