UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,685 lines (1,539 loc) 160 kB
import BoundingRectangle from "../Core/BoundingRectangle.js"; import BoundingSphere from "../Core/BoundingSphere.js"; import BoxGeometry from "../Core/BoxGeometry.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Cartographic from "../Core/Cartographic.js"; import Check from "../Core/Check.js"; import clone from "../Core/clone.js"; import Color from "../Core/Color.js"; import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js"; import createGuid from "../Core/createGuid.js"; import CullingVolume from "../Core/CullingVolume.js"; import Frozen from "../Core/Frozen.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; import DeveloperError from "../Core/DeveloperError.js"; import Ellipsoid from "../Core/Ellipsoid.js"; import EllipsoidGeometry from "../Core/EllipsoidGeometry.js"; import Event from "../Core/Event.js"; import GeographicProjection from "../Core/GeographicProjection.js"; import GeometryInstance from "../Core/GeometryInstance.js"; import GeometryPipeline from "../Core/GeometryPipeline.js"; import HeightReference from "./HeightReference.js"; import Intersect from "../Core/Intersect.js"; import JulianDate from "../Core/JulianDate.js"; import CesiumMath from "../Core/Math.js"; import Matrix4 from "../Core/Matrix4.js"; import mergeSort from "../Core/mergeSort.js"; import Occluder from "../Core/Occluder.js"; import OrthographicFrustum from "../Core/OrthographicFrustum.js"; import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js"; import PerspectiveFrustum from "../Core/PerspectiveFrustum.js"; import PerspectiveOffCenterFrustum from "../Core/PerspectiveOffCenterFrustum.js"; import Rectangle from "../Core/Rectangle.js"; import RequestScheduler from "../Core/RequestScheduler.js"; import TaskProcessor from "../Core/TaskProcessor.js"; import Transforms from "../Core/Transforms.js"; import ClearCommand from "../Renderer/ClearCommand.js"; import ComputeEngine from "../Renderer/ComputeEngine.js"; import Context from "../Renderer/Context.js"; import ContextLimits from "../Renderer/ContextLimits.js"; import Pass from "../Renderer/Pass.js"; import RenderState from "../Renderer/RenderState.js"; import Atmosphere from "./Atmosphere.js"; import BrdfLutGenerator from "./BrdfLutGenerator.js"; import Camera from "./Camera.js"; import Cesium3DTilePass from "./Cesium3DTilePass.js"; import Cesium3DTilePassState from "./Cesium3DTilePassState.js"; import CreditDisplay from "./CreditDisplay.js"; import DebugCameraPrimitive from "./DebugCameraPrimitive.js"; import DepthPlane from "./DepthPlane.js"; import DerivedCommand from "./DerivedCommand.js"; import DeviceOrientationCameraController from "./DeviceOrientationCameraController.js"; import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js"; import Fog from "./Fog.js"; import FrameState from "./FrameState.js"; import GlobeTranslucencyState from "./GlobeTranslucencyState.js"; import InvertClassification from "./InvertClassification.js"; import JobScheduler from "./JobScheduler.js"; import MapMode2D from "./MapMode2D.js"; import PerformanceDisplay from "./PerformanceDisplay.js"; import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js"; import Picking from "./Picking.js"; import PostProcessStageCollection from "./PostProcessStageCollection.js"; import Primitive from "./Primitive.js"; import PrimitiveCollection from "./PrimitiveCollection.js"; import SceneMode from "./SceneMode.js"; import SceneTransforms from "./SceneTransforms.js"; import SceneTransitioner from "./SceneTransitioner.js"; import ScreenSpaceCameraController from "./ScreenSpaceCameraController.js"; import ShadowMap from "./ShadowMap.js"; import SharedContext from "../Renderer/SharedContext.js"; import SpecularEnvironmentCubeMap from "./SpecularEnvironmentCubeMap.js"; import StencilConstants from "./StencilConstants.js"; import SunLight from "./SunLight.js"; import SunPostProcess from "./SunPostProcess.js"; import TweenCollection from "./TweenCollection.js"; import View from "./View.js"; import DebugInspector from "./DebugInspector.js"; import VoxelCell from "./VoxelCell.js"; import VoxelPrimitive from "./VoxelPrimitive.js"; import getMetadataClassProperty from "./getMetadataClassProperty.js"; import PickedMetadataInfo from "./PickedMetadataInfo.js"; import getMetadataProperty from "./getMetadataProperty.js"; const requestRenderAfterFrame = function (scene) { return function () { scene.frameState.afterRender.push(function () { scene.requestRender(); }); }; }; /** * The container for all 3D graphical objects and state in a Cesium virtual scene. Generally, * a scene is not created directly; instead, it is implicitly created by {@link CesiumWidget}. * * @alias Scene * @constructor * * @param {object} options Object with the following properties: * @param {HTMLCanvasElement} options.canvas The HTML canvas element to create the scene for. * @param {ContextOptions} [options.contextOptions] Context and WebGL creation properties. * @param {Element} [options.creditContainer] The HTML element in which the credits will be displayed. * @param {Element} [options.creditViewport] The HTML element in which to display the credit popup. If not specified, the viewport will be a added as a sibling of the canvas. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.default] The default ellipsoid. If not specified, the default ellipsoid is used. * @param {MapProjection} [options.mapProjection=new GeographicProjection(options.ellipsoid)] The map projection to use in 2D and Columbus View modes. * @param {boolean} [options.orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency. * @param {boolean} [options.scene3DOnly=false] If true, optimizes memory use and performance for 3D mode but disables the ability to use 2D or Columbus View. * @param {boolean} [options.shadows=false] Determines if shadows are cast by light sources. * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. * @param {boolean} [options.requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling improves performance of the application, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @param {number} [options.maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @param {number} [options.depthPlaneEllipsoidOffset=0.0] Adjust the DepthPlane to address rendering artefacts below ellipsoid zero elevation. * @param {number} [options.msaaSamples=4] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets. Set to 1 to disable MSAA. * * @see CesiumWidget * @see {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes} * * @exception {DeveloperError} options and options.canvas are required. * * @example * // Create scene without anisotropic texture filtering * const scene = new Cesium.Scene({ * canvas : canvas, * contextOptions : { * allowTextureFilterAnisotropic : false * } * }); */ function Scene(options) { options = options ?? Frozen.EMPTY_OBJECT; const canvas = options.canvas; let creditContainer = options.creditContainer; let creditViewport = options.creditViewport; //>>includeStart('debug', pragmas.debug); if (!defined(canvas)) { throw new DeveloperError("options and options.canvas are required."); } //>>includeEnd('debug'); const countReferences = options.contextOptions instanceof SharedContext; if (countReferences) { this._context = options.contextOptions.createSceneContext(canvas); } else { const contextOptions = clone(options.contextOptions); this._context = new Context(canvas, contextOptions); } const context = this._context; const hasCreditContainer = defined(creditContainer); if (!hasCreditContainer) { creditContainer = document.createElement("div"); creditContainer.style.position = "absolute"; creditContainer.style.bottom = "0"; creditContainer.style["text-shadow"] = "0 0 2px #000000"; creditContainer.style.color = "#ffffff"; creditContainer.style["font-size"] = "10px"; creditContainer.style["padding-right"] = "5px"; canvas.parentNode.appendChild(creditContainer); } if (!defined(creditViewport)) { creditViewport = canvas.parentNode; } this._id = createGuid(); this._jobScheduler = new JobScheduler(); this._frameState = new FrameState( context, new CreditDisplay(creditContainer, "•", creditViewport), this._jobScheduler, ); this._frameState.scene3DOnly = options.scene3DOnly ?? false; this._removeCreditContainer = !hasCreditContainer; this._creditContainer = creditContainer; this._canvas = canvas; this._computeEngine = new ComputeEngine(context); this._ellipsoid = options.ellipsoid ?? Ellipsoid.default; this._globe = undefined; this._globeTranslucencyState = new GlobeTranslucencyState(); this._primitives = new PrimitiveCollection({ countReferences }); this._groundPrimitives = new PrimitiveCollection({ countReferences }); this._globeHeight = undefined; this._globeHeightDirty = true; this._cameraUnderground = false; this._removeUpdateHeightCallback = undefined; this._logDepthBuffer = Scene.defaultLogDepthBuffer && context.fragmentDepth; this._logDepthBufferDirty = true; this._tweens = new TweenCollection(); this._shaderFrameCount = 0; this._sunPostProcess = undefined; this._computeCommandList = []; this._overlayCommandList = []; this._useOIT = options.orderIndependentTranslucency ?? true; /** * The function that will be used for executing translucent commands when * useOIT is true. This is created once in * obtainTranslucentCommandExecutionFunction, then cached here. * @private */ this._executeOITFunction = undefined; this._depthPlane = new DepthPlane(options.depthPlaneEllipsoidOffset); this._clearColorCommand = new ClearCommand({ color: new Color(), stencil: 0, owner: this, }); this._depthClearCommand = new ClearCommand({ depth: 1.0, owner: this, }); this._stencilClearCommand = new ClearCommand({ stencil: 0, }); this._classificationStencilClearCommand = new ClearCommand({ stencil: 0, renderState: RenderState.fromCache({ stencilMask: StencilConstants.CLASSIFICATION_MASK, }), }); this._depthOnlyRenderStateCache = {}; this._transitioner = new SceneTransitioner(this); this._preUpdate = new Event(); this._postUpdate = new Event(); this._renderError = new Event(); this._preRender = new Event(); this._postRender = new Event(); this._minimumDisableDepthTestDistance = 0.0; this._debugInspector = new DebugInspector(); this._msaaSamples = options.msaaSamples ?? 4; /** * Exceptions occurring in <code>render</code> are always caught in order to raise the * <code>renderError</code> event. If this property is true, the error is rethrown * after the event is raised. If this property is false, the <code>render</code> function * returns normally after raising the event. * * @type {boolean} * @default false */ this.rethrowRenderErrors = false; /** * Determines whether or not to instantly complete the * scene transition animation on user input. * * @type {boolean} * @default true */ this.completeMorphOnUserInput = true; /** * The event fired at the beginning of a scene transition. * @type {Event} * @default Event() */ this.morphStart = new Event(); /** * The event fired at the completion of a scene transition. * @type {Event} * @default Event() */ this.morphComplete = new Event(); /** * The {@link SkyBox} used to draw the stars. * * @type {SkyBox | undefined} * @default undefined * * @see Scene#backgroundColor */ this.skyBox = undefined; /** * The sky atmosphere drawn around the globe. * * @type {SkyAtmosphere | undefined} * @default undefined */ this.skyAtmosphere = undefined; /** * The {@link Sun}. * * @type {Sun | undefined} * @default undefined */ this.sun = undefined; /** * Uses a bloom filter on the sun when enabled. * * @type {boolean} * @default true */ this.sunBloom = true; this._sunBloom = undefined; /** * The {@link Moon} * * @type {Moon | undefined} * @default undefined */ this.moon = undefined; /** * The background color, which is only visible if there is no sky box, i.e., {@link Scene#skyBox} is <code>undefined</code>. * * @type {Color} * @default {@link Color.BLACK} * * @see Scene#skyBox */ this.backgroundColor = Color.clone(Color.BLACK); this._mode = SceneMode.SCENE3D; this._mapProjection = defined(options.mapProjection) ? options.mapProjection : new GeographicProjection(this._ellipsoid); /** * The current morph transition time between 2D/Columbus View and 3D, * with 0.0 being 2D or Columbus View and 1.0 being 3D. * * @type {number} * @default 1.0 */ this.morphTime = 1.0; /** * The far-to-near ratio of the multi-frustum when using a normal depth buffer. * <p> * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used * when {@link Scene#logarithmicDepthBuffer} is <code>false</code>. When <code>logarithmicDepthBuffer</code> is * <code>true</code>, use {@link Scene#logarithmicDepthFarToNearRatio}. * </p> * * @type {number} * @default 1000.0 */ this.farToNearRatio = 1000.0; /** * The far-to-near ratio of the multi-frustum when using a logarithmic depth buffer. * <p> * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used * when {@link Scene#logarithmicDepthBuffer} is <code>true</code>. When <code>logarithmicDepthBuffer</code> is * <code>false</code>, use {@link Scene#farToNearRatio}. * </p> * * @type {number} * @default 1e9 */ this.logarithmicDepthFarToNearRatio = 1e9; /** * Determines the uniform depth size in meters of each frustum of the multifrustum in 2D. If a primitive or model close * to the surface shows z-fighting, decreasing this will eliminate the artifact, but decrease performance. On the * other hand, increasing this will increase performance but may cause z-fighting among primitives close to the surface. * * @type {number} * @default 1.75e6 */ this.nearToFarDistance2D = 1.75e6; /** * The vertical exaggeration of the scene. * When set to 1.0, no exaggeration is applied. * * @type {number} * @default 1.0 */ this.verticalExaggeration = 1.0; /** * The reference height for vertical exaggeration of the scene. * When set to 0.0, the exaggeration is applied relative to the ellipsoid surface. * * @type {number} * @default 0.0 */ this.verticalExaggerationRelativeHeight = 0.0; /** * This property is for debugging only; it is not for production use. * <p> * A function that determines what commands are executed. As shown in the examples below, * the function receives the command's <code>owner</code> as an argument, and returns a boolean indicating if the * command should be executed. * </p> * <p> * The default is <code>undefined</code>, indicating that all commands are executed. * </p> * * @type {Function | undefined} * * @default undefined * * @example * // Do not execute any commands. * scene.debugCommandFilter = function(command) { * return false; * }; * * // Execute only the billboard's commands. That is, only draw the billboard. * const billboards = new Cesium.BillboardCollection(); * scene.debugCommandFilter = function(command) { * return command.owner === billboards; * }; */ this.debugCommandFilter = undefined; /** * This property is for debugging only; it is not for production use. * <p> * When <code>true</code>, commands are randomly shaded. This is useful * for performance analysis to see what parts of a scene or model are * command-dense and could benefit from batching. * </p> * * @type {boolean} * * @default false */ this.debugShowCommands = false; /** * This property is for debugging only; it is not for production use. * <p> * When <code>true</code>, commands are shaded based on the frustums they * overlap. Commands in the closest frustum are tinted red, commands in * the next closest are green, and commands in the farthest frustum are * blue. If a command overlaps more than one frustum, the color components * are combined, e.g., a command overlapping the first two frustums is tinted * yellow. * </p> * * @type {boolean} * * @default false */ this.debugShowFrustums = false; /** * This property is for debugging only; it is not for production use. * <p> * Displays frames per second and time between frames. * </p> * * @type {boolean} * * @default false */ this.debugShowFramesPerSecond = false; /** * This property is for debugging only; it is not for production use. * <p> * Indicates which frustum will have depth information displayed. * </p> * * @type {number} * * @default 1 */ this.debugShowDepthFrustum = 1; /** * This property is for debugging only; it is not for production use. * <p> * When <code>true</code>, draws outlines to show the boundaries of the camera frustums * </p> * * @type {boolean} * * @default false */ this.debugShowFrustumPlanes = false; this._debugShowFrustumPlanes = false; this._debugFrustumPlanes = undefined; /** * When <code>true</code>, enables picking using the depth buffer. * * @type {boolean} * @default true */ this.useDepthPicking = true; /** * When <code>true</code>, enables picking translucent geometry using the depth buffer. Note that {@link Scene#useDepthPicking} must also be true for enabling this to work. * * <p> * There is a decrease in performance when enabled. There are extra draw calls to write depth for * translucent geometry. * </p> * * @example * // picking the position of a translucent primitive * viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) { * const pickedFeature = viewer.scene.pick(movement.position); * if (!Cesium.defined(pickedFeature)) { * // nothing picked * return; * } * const worldPosition = viewer.scene.pickPosition(movement.position); * }, Cesium.ScreenSpaceEventType.LEFT_CLICK); * * @type {boolean} * @default false */ this.pickTranslucentDepth = false; /** * The time in milliseconds to wait before checking if the camera has not moved and fire the cameraMoveEnd event. * @type {number} * @default 500.0 * @private */ this.cameraEventWaitTime = 500.0; /** * Settings for atmosphere lighting effects affecting 3D Tiles and model rendering. This is not to be confused with * {@link Scene#skyAtmosphere} which is responsible for rendering the sky. * * @type {Atmosphere} */ this.atmosphere = new Atmosphere(); /** * Blends the atmosphere to geometry far from the camera for horizon views. Allows for additional * performance improvements by rendering less geometry and dispatching less terrain requests. * * Disbaled by default if an ellipsoid other than WGS84 is used. * @type {Fog} */ this.fog = new Fog(); this.fog.enabled = Ellipsoid.WGS84.equals(this._ellipsoid); if (!Ellipsoid.WGS84.equals(this._ellipsoid)) { Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees( -45.0, -45.0, 45.0, 45.0, ); } this._shadowMapCamera = new Camera(this); /** * The shadow map for the scene's light source. When enabled, models, primitives, and the globe may cast and receive shadows. * @type {ShadowMap} */ this.shadowMap = new ShadowMap({ context: context, lightCamera: this._shadowMapCamera, enabled: options.shadows ?? false, }); /** * When <code>false</code>, 3D Tiles will render normally. When <code>true</code>, classified 3D Tile geometry will render normally and * unclassified 3D Tile geometry will render with the color multiplied by {@link Scene#invertClassificationColor}. * @type {boolean} * @default false */ this.invertClassification = false; /** * The highlight color of unclassified 3D Tile geometry when {@link Scene#invertClassification} is <code>true</code>. * <p>When the color's alpha is less than 1.0, the unclassified portions of the 3D Tiles will not blend correctly with the classified positions of the 3D Tiles.</p> * <p>Also, when the color's alpha is less than 1.0, the WEBGL_depth_texture and EXT_frag_depth WebGL extensions must be supported.</p> * @type {Color} * @default Color.WHITE */ this.invertClassificationColor = Color.clone(Color.WHITE); this._actualInvertClassificationColor = Color.clone( this._invertClassificationColor, ); this._invertClassification = new InvertClassification(); /** * The focal length for use when with cardboard or WebVR. * @type {number} */ this.focalLength = undefined; /** * The eye separation distance in meters for use with cardboard or WebVR. * @type {number} */ this.eyeSeparation = undefined; /** * Post processing effects applied to the final render. * @type {PostProcessStageCollection} */ this.postProcessStages = new PostProcessStageCollection(); this._brdfLutGenerator = new BrdfLutGenerator(); this._performanceDisplay = undefined; this._debugVolume = undefined; this._screenSpaceCameraController = new ScreenSpaceCameraController(this); this._cameraUnderground = false; this._mapMode2D = options.mapMode2D ?? MapMode2D.INFINITE_SCROLL; // Keeps track of the state of a frame. FrameState is the state across // the primitives of the scene. This state is for internally keeping track // of celestial and environment effects that need to be updated/rendered in // a certain order as well as updating/tracking framebuffer usage. this._environmentState = { skyBoxCommand: undefined, skyAtmosphereCommand: undefined, sunDrawCommand: undefined, sunComputeCommand: undefined, moonCommand: undefined, isSunVisible: false, isMoonVisible: false, isReadyForAtmosphere: false, isSkyAtmosphereVisible: false, clearGlobeDepth: false, useDepthPlane: false, renderTranslucentDepthForPick: false, originalFramebuffer: undefined, useGlobeDepthFramebuffer: false, useOIT: false, useInvertClassification: false, usePostProcess: false, usePostProcessSelected: false, useWebVR: false, }; this._useWebVR = false; this._cameraVR = undefined; this._aspectRatioVR = undefined; /** * When <code>true</code>, rendering a frame will only occur when needed as determined by changes within the scene. * Enabling improves performance of the application, but requires using {@link Scene#requestRender} * to render a new frame explicitly in this mode. This will be necessary in many cases after making changes * to the scene in other parts of the API. * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#maximumRenderTimeChange * @see Scene#requestRender * * @type {boolean} * @default false */ this.requestRenderMode = options.requestRenderMode ?? false; this._renderRequested = true; /** * If {@link Scene#requestRenderMode} is <code>true</code>, this value defines the maximum change in * simulation time allowed before a render is requested. Lower values increase the number of frames rendered * and higher values decrease the number of frames rendered. If <code>undefined</code>, changes to * the simulation time will never request a render. * This value impacts the rate of rendering for changes in the scene like lighting, entity property updates, * and animations. * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#requestRenderMode * * @type {number} * @default 0.0 */ this.maximumRenderTimeChange = options.maximumRenderTimeChange ?? 0.0; this._lastRenderTime = undefined; this._frameRateMonitor = undefined; this._removeRequestListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener( requestRenderAfterFrame(this), ); this._removeTaskProcessorListenerCallback = TaskProcessor.taskCompletedEvent.addEventListener( requestRenderAfterFrame(this), ); this._removeGlobeCallbacks = []; this._removeTerrainProviderReadyListener = undefined; const viewport = new BoundingRectangle( 0, 0, context.drawingBufferWidth, context.drawingBufferHeight, ); const camera = new Camera(this); if (this._logDepthBuffer) { camera.frustum.near = 0.1; camera.frustum.far = 10000000000.0; } /** * The camera view for the scene camera flight destination. Used for preloading flight destination tiles. * @type {Camera} * @private */ this.preloadFlightCamera = new Camera(this); /** * The culling volume for the scene camera flight destination. Used for preloading flight destination tiles. * @type {CullingVolume} * @private */ this.preloadFlightCullingVolume = undefined; this._picking = new Picking(this); this._defaultView = new View(this, camera, viewport); this._view = this._defaultView; this._hdr = undefined; this._hdrDirty = undefined; this.highDynamicRange = false; this.gamma = 2.2; /** * The spherical harmonic coefficients for image-based lighting of PBR models. * @type {Cartesian3[]} */ this.sphericalHarmonicCoefficients = undefined; /** * The url to the KTX2 file containing the specular environment map and convoluted mipmaps for image-based lighting of PBR models. * @type {string} */ this.specularEnvironmentMaps = undefined; this._specularEnvironmentCubeMap = undefined; /** * The light source for shading. Defaults to a directional light from the Sun. * @type {Light} */ this.light = new SunLight(); // Give frameState, camera, and screen space camera controller initial state before rendering updateFrameNumber(this, 0.0, JulianDate.now()); this.updateFrameState(); this.initializeFrame(); } /** * Use this to set the default value for {@link Scene#logarithmicDepthBuffer} in newly constructed Scenes * This property relies on fragmentDepth being supported. */ Scene.defaultLogDepthBuffer = true; function updateGlobeListeners(scene, globe) { for (let i = 0; i < scene._removeGlobeCallbacks.length; ++i) { scene._removeGlobeCallbacks[i](); } scene._removeGlobeCallbacks.length = 0; const removeGlobeCallbacks = []; if (defined(globe)) { removeGlobeCallbacks.push( globe.imageryLayersUpdatedEvent.addEventListener( requestRenderAfterFrame(scene), ), ); removeGlobeCallbacks.push( globe.terrainProviderChanged.addEventListener( requestRenderAfterFrame(scene), ), ); } scene._removeGlobeCallbacks = removeGlobeCallbacks; } Object.defineProperties(Scene.prototype, { /** * Gets the canvas element to which this scene is bound. * @memberof Scene.prototype * * @type {HTMLCanvasElement} * @readonly */ canvas: { get: function () { return this._canvas; }, }, /** * The drawingBufferHeight of the underlying GL context. * @memberof Scene.prototype * * @type {number} * @readonly * * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferHeight|drawingBufferHeight} */ drawingBufferHeight: { get: function () { return this._context.drawingBufferHeight; }, }, /** * The drawingBufferWidth of the underlying GL context. * @memberof Scene.prototype * * @type {number} * @readonly * * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferWidth|drawingBufferWidth} */ drawingBufferWidth: { get: function () { return this._context.drawingBufferWidth; }, }, /** * The maximum aliased line width, in pixels, supported by this WebGL implementation. It will be at least one. * @memberof Scene.prototype * * @type {number} * @readonly * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with <code>ALIASED_LINE_WIDTH_RANGE</code>. */ maximumAliasedLineWidth: { get: function () { return ContextLimits.maximumAliasedLineWidth; }, }, /** * The maximum length in pixels of one edge of a cube map, supported by this WebGL implementation. It will be at least 16. * @memberof Scene.prototype * * @type {number} * @readonly * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with <code>GL_MAX_CUBE_MAP_TEXTURE_SIZE</code>. */ maximumCubeMapSize: { get: function () { return ContextLimits.maximumCubeMapSize; }, }, /** * Returns <code>true</code> if the {@link Scene#pickPosition} function is supported. * @memberof Scene.prototype * * @type {boolean} * @readonly * * @see Scene#pickPosition */ pickPositionSupported: { get: function () { return this._context.depthTexture; }, }, /** * Returns <code>true</code> if the {@link Scene#sampleHeight} and {@link Scene#sampleHeightMostDetailed} functions are supported. * @memberof Scene.prototype * * @type {boolean} * @readonly * * @see Scene#sampleHeight * @see Scene#sampleHeightMostDetailed */ sampleHeightSupported: { get: function () { return this._context.depthTexture; }, }, /** * Returns <code>true</code> if the {@link Scene#clampToHeight} and {@link Scene#clampToHeightMostDetailed} functions are supported. * @memberof Scene.prototype * * @type {boolean} * @readonly * * @see Scene#clampToHeight * @see Scene#clampToHeightMostDetailed */ clampToHeightSupported: { get: function () { return this._context.depthTexture; }, }, /** * Returns <code>true</code> if the {@link Scene#invertClassification} is supported. * @memberof Scene.prototype * * @type {boolean} * @readonly * * @see Scene#invertClassification */ invertClassificationSupported: { get: function () { return this._context.depthTexture; }, }, /** * Returns <code>true</code> if specular environment maps are supported. * @memberof Scene.prototype * * @type {boolean} * @readonly * * @see Scene#specularEnvironmentMaps */ specularEnvironmentMapsSupported: { get: function () { return SpecularEnvironmentCubeMap.isSupported(this._context); }, }, /** * The ellipsoid. If not specified, the default ellipsoid is used. * @memberof Scene.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets or sets the depth-test ellipsoid. * @memberof Scene.prototype * * @type {Globe} */ globe: { get: function () { return this._globe; }, set: function (globe) { this._globe = this._globe && this._globe.destroy(); this._globe = globe; updateGlobeListeners(this, globe); }, }, /** * Gets the collection of primitives. * @memberof Scene.prototype * * @type {PrimitiveCollection} * @readonly */ primitives: { get: function () { return this._primitives; }, }, /** * Gets the collection of ground primitives. * @memberof Scene.prototype * * @type {PrimitiveCollection} * @readonly */ groundPrimitives: { get: function () { return this._groundPrimitives; }, }, /** * Gets or sets the camera. * @memberof Scene.prototype * * @type {Camera} * @readonly */ camera: { get: function () { return this._view.camera; }, set: function (camera) { // For internal use only. Documentation is still @readonly. this._view.camera = camera; }, }, /** * Gets or sets the view. * @memberof Scene.prototype * * @type {View} * @readonly * * @private */ view: { get: function () { return this._view; }, set: function (view) { // For internal use only. Documentation is still @readonly. this._view = view; }, }, /** * Gets the default view. * @memberof Scene.prototype * * @type {View} * @readonly * * @private */ defaultView: { get: function () { return this._defaultView; }, }, /** * Gets picking functions and state * @memberof Scene.prototype * * @type {Picking} * @readonly * * @private */ picking: { get: function () { return this._picking; }, }, /** * Gets the controller for camera input handling. * @memberof Scene.prototype * * @type {ScreenSpaceCameraController} * @readonly */ screenSpaceCameraController: { get: function () { return this._screenSpaceCameraController; }, }, /** * Get the map projection to use in 2D and Columbus View modes. * @memberof Scene.prototype * * @type {MapProjection} * @readonly * * @default new GeographicProjection() */ mapProjection: { get: function () { return this._mapProjection; }, }, /** * Gets the job scheduler * @memberof Scene.prototype * @type {JobScheduler} * @readonly * * @private */ jobScheduler: { get: function () { return this._jobScheduler; }, }, /** * Gets state information about the current scene. If called outside of a primitive's <code>update</code> * function, the previous frame's state is returned. * @memberof Scene.prototype * * @type {FrameState} * @readonly * * @private */ frameState: { get: function () { return this._frameState; }, }, /** * Gets the environment state. * @memberof Scene.prototype * * @type {EnvironmentState} * @readonly * * @private */ environmentState: { get: function () { return this._environmentState; }, }, /** * Gets the collection of tweens taking place in the scene. * @memberof Scene.prototype * * @type {TweenCollection} * @readonly * * @private */ tweens: { get: function () { return this._tweens; }, }, /** * Gets the collection of image layers that will be rendered on the globe. * @memberof Scene.prototype * * @type {ImageryLayerCollection} * @readonly */ imageryLayers: { get: function () { if (!defined(this.globe)) { return undefined; } return this.globe.imageryLayers; }, }, /** * The terrain provider providing surface geometry for the globe. * @memberof Scene.prototype * * @type {TerrainProvider} */ terrainProvider: { get: function () { if (!defined(this.globe)) { return undefined; } return this.globe.terrainProvider; }, set: function (terrainProvider) { // Cancel any in-progress terrain update this._removeTerrainProviderReadyListener = this._removeTerrainProviderReadyListener && this._removeTerrainProviderReadyListener(); if (defined(this.globe)) { this.globe.terrainProvider = terrainProvider; } }, }, /** * Gets an event that's raised when the terrain provider is changed * @memberof Scene.prototype * * @type {Event} * @readonly */ terrainProviderChanged: { get: function () { if (!defined(this.globe)) { return undefined; } return this.globe.terrainProviderChanged; }, }, /** * Gets the event that will be raised before the scene is updated or rendered. Subscribers to the event * receive the Scene instance as the first parameter and the current time as the second parameter. * @memberof Scene.prototype * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#postUpdate * @see Scene#preRender * @see Scene#postRender * * @type {Event} * @readonly */ preUpdate: { get: function () { return this._preUpdate; }, }, /** * Gets the event that will be raised immediately after the scene is updated and before the scene is rendered. * Subscribers to the event receive the Scene instance as the first parameter and the current time as the second * parameter. * @memberof Scene.prototype * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#preUpdate * @see Scene#preRender * @see Scene#postRender * * @type {Event} * @readonly */ postUpdate: { get: function () { return this._postUpdate; }, }, /** * Gets the event that will be raised when an error is thrown inside the <code>render</code> function. * The Scene instance and the thrown error are the only two parameters passed to the event handler. * By default, errors are not rethrown after this event is raised, but that can be changed by setting * the <code>rethrowRenderErrors</code> property. * @memberof Scene.prototype * * @type {Event} * @readonly */ renderError: { get: function () { return this._renderError; }, }, /** * Gets the event that will be raised after the scene is updated and immediately before the scene is rendered. * Subscribers to the event receive the Scene instance as the first parameter and the current time as the second * parameter. * @memberof Scene.prototype * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#preUpdate * @see Scene#postUpdate * @see Scene#postRender * * @type {Event} * @readonly */ preRender: { get: function () { return this._preRender; }, }, /** * Gets the event that will be raised immediately after the scene is rendered. Subscribers to the event * receive the Scene instance as the first parameter and the current time as the second parameter. * @memberof Scene.prototype * * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering} * @see Scene#preUpdate * @see Scene#postUpdate * @see Scene#postRender * * @type {Event} * @readonly */ postRender: { get: function () { return this._postRender; }, }, /** * Gets the simulation time when the scene was last rendered. Returns <code>undefined</code> * if the scene has not yet been rendered. * @memberof Scene.prototype * * @type {JulianDate | undefined} * @readonly */ lastRenderTime: { get: function () { return this._lastRenderTime; }, }, /** * @memberof Scene.prototype * @private * @readonly */ context: { get: function () { return this._context; }, }, /** * This property is for debugging only; it is not for production use. * <p> * When {@link Scene.debugShowFrustums} is <code>true</code>, this contains * properties with statistics about the number of command execute per frustum. * <code>totalCommands</code> is the total number of commands executed, ignoring * overlap. <code>commandsInFrustums</code> is an array with the number of times * commands are executed redundantly, e.g., how many commands overlap two or * three frustums. * </p> * * @memberof Scene.prototype * * @type {Object | undefined} * @readonly * * @default undefined */ debugFrustumStatistics: { get: function () { return this._view.debugFrustumStatistics; }, }, /** * Gets whether or not the scene is optimized for 3D only viewing. * @memberof Scene.prototype * @type {boolean} * @readonly */ scene3DOnly: { get: function () { return this._frameState.scene3DOnly; }, }, /** * Gets whether or not the scene has order independent translucency enabled. * Note that this only reflects the original construction option, and there are * other factors that could prevent OIT from functioning on a given system configuration. * @memberof Scene.prototype * @type {boolean} * @readonly */ orderIndependentTranslucency: { get: function () { return this._useOIT; }, }, /** * Gets the unique identifier for this scene. * @memberof Scene.prototype * @type {string} * @readonly */ id: { get: function () { return this._id; }, }, /** * Gets or sets the current mode of the scene. * @memberof Scene.prototype * @type {SceneMode} * @default {@link SceneMode.SCENE3D} */ mode: { get: function () { return this._mode; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (this.scene3DOnly && value !== SceneMode.SCENE3D) { throw new DeveloperError( "Only SceneMode.SCENE3D is valid when scene3DOnly is true.", ); } //>>includeEnd('debug'); if (value === SceneMode.SCENE2D) { this.morphTo2D(0); } else if (value === SceneMode.SCENE3D) { this.morphTo3D(0); } else if (value === SceneMode.COLUMBUS_VIEW) { this.morphToColumbusView(0); //>>includeStart('debug', pragmas.debug); } else { throw new DeveloperError( "value must be a valid SceneMode enumeration.", ); //>>includeEnd('debug'); } this._mode = value; }, }, /** * Gets the number of frustums used in the last frame. * @memberof Scene.prototype * @type {FrustumCommands[]} * * @private */ frustumCommandsList: { get: function () { return this._view.frustumCommandsList; }, }, /** * Gets the number of frustums used in the last frame. * @memberof Scene.prototype * @type {number} * * @private */ numberOfFrustums: { get: function () { return this._view.frustumCommandsList.length; }, }, /** * When <code>true</code>, splits the scene into two viewports with steroscopic views for the left and right eyes. * Used for cardboard and WebVR. * @memberof Scene.prototype * @type {boolean} * @default false */ useWebVR: { get: function () { return this._useWebVR; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (this.camera.frustum instanceof OrthographicFrustum) { throw new DeveloperError( "VR is unsupported with an orthographic projection.", ); } //>>includeEnd('debug'); this._useWebVR = value; if (this._useWebVR) { this._frameState.creditDisplay.container.style.visibility = "hidden"; this._cameraVR = new Camera(this); if (!defined(this._deviceOrientationCameraController)) { this._deviceOrientationCameraController = new DeviceOrientationCameraController(this); } this._aspectRatioVR = this.camera.frustum.aspectRatio; } else { this._frameState.creditDisplay.container.style.visibility = "visible"; this._cameraVR = undefined; this._deviceOrientationCameraController = this._deviceOrientationCameraController && !this._deviceOrientationCameraController.isDestroyed() && this._deviceOrientationCameraController.destroy(); this.camera.frustum.aspectRatio = this._aspectRatioVR; this.camera.frustum.xOffset = 0.0; } }, }, /** * Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. * @memberof Scene.prototype * @type {MapMode2D} * @readonly */ mapMode2D: { get: function () { return this._mapMode2D; }, }, /** * Gets or sets the position of the splitter within the viewport. Valid values are between 0.0 and 1.0. * @memberof Scene.prototype * * @type {number} */ splitPosition: { get: function () { return this._frameState.splitPosition; }, set: function (value) { this._frameState.splitPosition = value; }, }, /** * The distance from the camera at which to disable the depth test of billboards, labels and points * to, for example, prevent clipping against terrain. When set to zero, the depth test should always * be applied. When less than zero, the depth test should never be applied. Setting the disableDepthTestDistance * property of a billboard, label or point will override this value. * @memberof Scene.prototype * @type {number} * @default 0.0 */ minimumDisableDepthTestDistance: { get: function () { return this._minimumDisableDepthTestDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value) || value < 0.0) { throw new DeveloperError( "minimumDisableDepthTestDistance must be greater than or equal to 0.0.", ); } //>>includeEnd('debug'); this._minimumDisableDepthTestDistance = value; }, }, /** * Whether or not to use a logarithmic depth buffer. Enabling this option will allow for less frustums in the multi-frustum, * increasing performance. This property relies on fragmentDepth being supported. * @memberof Scene.prototype * @type {boolean} */ logarithmicDepthBuffer: { get: function () { return this._logDepthBuffer; }, set: function (value) { value = this._context.fragmentDepth && value; if (this._logDepthBuffer !== value) { this._logDepthBuffer = value; this._logDepthBufferDirty = true; } }, }, /** * The value used for gamma correction. This is only used when rendering with high dynamic range. * @memberof Scene.prototype * @type {number} * @default 2.2 */ gamma: { get: function () { return this._context.uniformState.gamma; }, set: function (value) { this._context.uniformState.gamma = value; }, }, /** * Whether or not to use high dynamic range rendering. * @memberof Scene.prototype * @type {boolean} * @default false */ highDynamicRange: { get: function () { return this._hdr; }, set: function (value) { const context = this._context; const hdr = value && context.depthTexture && (context.colorBufferFloat || context.colorBufferHalfFloat); this._hdrDirty = hdr !== this._hdr; this._hdr = hdr; }, }, /** * Whether or not high dynamic range rendering is supported. * @memberof Scene.prototype * @type {boolean} * @readonly * @default true */ highDynamicRangeSupported: { get: function () { const context = this._context; return ( context.depthTexture && (context.colorBufferFloat || context.colorBufferHalfFloat) ); }, }, /** * Whether or not the camera is underneath the globe. * @memberof Scene.prototype * @type {boolean} * @readonly * @default false */ cameraUnderground: { get: function () { return this._cameraUnderground; }, }, /** * The sample rate of multisample antialiasing (values greater than 1 enable MSAA). * @memberof Scene.prototype * @type {number} * @default 4 */ msaaSamples: { get: function () { return this._msaaSamples; }, set: function (value) { value = Math.min(value, ContextLimits.maximumSamples); this._msaaSamples = value; }, }, /** * Returns <code>true</code> if the Scene's context supports MSAA. * @memberof Scene.prototype * @type {boolean} * @readonly */ msaaSupported: { get: function () { return this._context.msaa; }, }, /** * Ratio between a pixel and a density-independent pixel. Provides a standard unit of * measure for real pixel measurements appropriate to a particular device. * * @memberof Scene.prototype * @type {number} * @default 1.0 * @private */ pixelRatio: { get: function () { return this._frameState.pixelRatio; }, set: function (value) { this._frameSta