cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
1,712 lines (1,566 loc) • 142 kB
JavaScript
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 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 defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.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 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 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 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 Fog from "./Fog.js";
import FrameState from "./FrameState.js";
import GlobeDepth from "./GlobeDepth.js";
import GlobeTranslucencyState from "./GlobeTranslucencyState.js";
import InvertClassification from "./InvertClassification.js";
import JobScheduler from "./JobScheduler.js";
import MapMode2D from "./MapMode2D.js";
import OctahedralProjectedCubeMap from "./OctahedralProjectedCubeMap.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 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";
var 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}.
* <p>
* <em><code>contextOptions</code> parameter details:</em>
* </p>
* <p>
* The default values are:
* <code>
* {
* webgl : {
* alpha : false,
* depth : true,
* stencil : false,
* antialias : true,
* powerPreference: 'high-performance',
* premultipliedAlpha : true,
* preserveDrawingBuffer : false,
* failIfMajorPerformanceCaveat : false
* },
* allowTextureFilterAnisotropic : true
* }
* </code>
* </p>
* <p>
* The <code>webgl</code> property corresponds to the {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes}
* object used to create the WebGL context.
* </p>
* <p>
* <code>webgl.alpha</code> defaults to false, which can improve performance compared to the standard WebGL default
* of true. If an application needs to composite Cesium above other HTML elements using alpha-blending, set
* <code>webgl.alpha</code> to true.
* </p>
* <p>
* The other <code>webgl</code> properties match the WebGL defaults for {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes}.
* </p>
* <p>
* <code>allowTextureFilterAnisotropic</code> defaults to true, which enables anisotropic texture filtering when the
* WebGL extension is supported. Setting this to false will improve performance, but hurt visual quality, especially for horizon views.
* </p>
*
* @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 {Object} [options.contextOptions] Context and WebGL creation properties. See details above.
* @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 {MapProjection} [options.mapProjection=new GeographicProjection()] 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 {Number} [options.terrainExaggeration=1.0] A scalar used to exaggerate the terrain. Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid.
* @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}.
*
* @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
* var scene = new Cesium.Scene({
* canvas : canvas,
* contextOptions : {
* allowTextureFilterAnisotropic : false
* }
* });
*/
function Scene(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var canvas = options.canvas;
var creditContainer = options.creditContainer;
var creditViewport = options.creditViewport;
var contextOptions = clone(options.contextOptions);
if (!defined(contextOptions)) {
contextOptions = {};
}
if (!defined(contextOptions.webgl)) {
contextOptions.webgl = {};
}
contextOptions.webgl.powerPreference = defaultValue(
contextOptions.webgl.powerPreference,
"high-performance"
);
//>>includeStart('debug', pragmas.debug);
if (!defined(canvas)) {
throw new DeveloperError("options and options.canvas are required.");
}
//>>includeEnd('debug');
var hasCreditContainer = defined(creditContainer);
var context = new Context(canvas, contextOptions);
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 = defaultValue(options.scene3DOnly, false);
this._removeCreditContainer = !hasCreditContainer;
this._creditContainer = creditContainer;
this._canvas = canvas;
this._context = context;
this._computeEngine = new ComputeEngine(context);
this._globe = undefined;
this._globeTranslucencyState = new GlobeTranslucencyState();
this._primitives = new PrimitiveCollection();
this._groundPrimitives = new PrimitiveCollection();
this._globeHeight = undefined;
this._cameraUnderground = false;
this._logDepthBuffer = context.fragmentDepth;
this._logDepthBufferDirty = true;
this._tweens = new TweenCollection();
this._shaderFrameCount = 0;
this._sunPostProcess = undefined;
this._computeCommandList = [];
this._overlayCommandList = [];
this._useOIT = defaultValue(options.orderIndependentTranslucency, true);
this._executeOITFunction = undefined;
this._depthPlane = new DepthPlane();
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();
/**
* 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}
* @default undefined
*
* @see Scene#backgroundColor
*/
this.skyBox = undefined;
/**
* The sky atmosphere drawn around the globe.
*
* @type {SkyAtmosphere}
* @default undefined
*/
this.skyAtmosphere = undefined;
/**
* The {@link Sun}.
*
* @type {Sun}
* @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
* @default undefined
*/
this.moon = undefined;
/**
* The background color, which is only visible if there is no sky box, i.e., {@link Scene#skyBox} is undefined.
*
* @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();
/**
* 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;
/**
* 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
*
* @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.
* var 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>
* Displays depth information for the indicated frustum.
* </p>
*
* @type Boolean
*
* @default false
*/
this.debugShowGlobeDepth = 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>
* Render must be called between picks.
* <br>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) {
* var pickedFeature = viewer.scene.pick(movement.position);
* if (!Cesium.defined(pickedFeature)) {
* // nothing picked
* return;
* }
* viewer.scene.render();
* var 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;
/**
* 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.
* @type {Fog}
*/
this.fog = new Fog();
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: defaultValue(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._terrainExaggeration = defaultValue(options.terrainExaggeration, 1.0);
this._performanceDisplay = undefined;
this._debugVolume = undefined;
this._screenSpaceCameraController = new ScreenSpaceCameraController(this);
this._cameraUnderground = false;
this._mapMode2D = defaultValue(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,
separatePrimitiveFramebuffer: 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 = defaultValue(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 = defaultValue(
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 = [];
var viewport = new BoundingRectangle(
0,
0,
context.drawingBufferWidth,
context.drawingBufferHeight
);
var 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 KTX file containing the specular environment map and convoluted mipmaps for image-based lighting of PBR models.
* @type {String}
*/
this.specularEnvironmentMaps = undefined;
this._specularEnvironmentMapAtlas = 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();
}
function updateGlobeListeners(scene, globe) {
for (var i = 0; i < scene._removeGlobeCallbacks.length; ++i) {
scene._removeGlobeCallbacks[i]();
}
scene._removeGlobeCallbacks.length = 0;
var 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 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}
*/
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 OctahedralProjectedCubeMap.isSupported(this._context);
},
},
/**
* 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) {
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 undefined if the scene has not yet been
* rendered.
* @memberof Scene.prototype
*
* @type {JulianDate}
* @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}
* @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;
},
},
/**
* Gets the scalar used to exaggerate the terrain.
* @memberof Scene.prototype
* @type {Number}
* @readonly
*/
terrainExaggeration: {
get: function () {
return this._terrainExaggeration;
},
},
/**
* 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 Imagery splitter within the viewport. Valid values are between 0.0 and 1.0.
* @memberof Scene.prototype
*
* @type {Number}
*/
imagerySplitPosition: {
get: function () {
return this._frameState.imagerySplitPosition;
},
set: function (value) {
this._frameState.imagerySplitPosition = 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 true
*/
highDynamicRange: {
get: function () {
return this._hdr;
},
set: function (value) {
var context = this._context;
var 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 () {
var 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;
},
},
/**
* 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._frameState.pixelRatio = value;
},
},
/**
* @private
*/
opaqueFrustumNearOffset: {
get: function () {
return 0.9999;
},
},
/**
* @private
*/
globeHeight: {
get: function () {
return this._globeHeight;
},
},
});
/**
* Determines if a compressed texture format is supported.
* @param {String} format The texture format. May be the name of the format or the WebGL extension name, e.g. s3tc or WEBGL_compressed_texture_s3tc.
* @return {boolean} Whether or not the format is supported.
*/
Scene.prototype.getCompressedTextureFormatSupported = function (format) {
var context = this.context;
return (
((format === "WEBGL_compressed_texture_s3tc" || format === "s3tc") &&
context.s3tc) ||
((format === "WEBGL_compressed_texture_pvrtc" || format === "pvrtc") &&
context.pvrtc) ||
((format === "WEBGL_compressed_texture_etc1" || format === "etc1") &&
context.etc1)
);
};
function updateDerivedCommands(scene, command, shadowsDirty) {
var frameState = scene._frameState;
var context = scene._context;
var oit = scene._view.oit;
var lightShadowMaps = frameState.shadowState.lightShadowMaps;
var lightShadowsEnabled = frameState.shadowState.lightShadowsEnabled;
var derivedCommands = command.derivedCommands;
if (defined(command.pickId)) {
derivedCommands.picking = DerivedCommand.createPickDerivedCommand(
scene,
command,
context,
derivedCommands.picking
);
}
if (!command.pickOnly) {
derivedCommands.depth = DerivedCommand.createDepthOnlyDerivedCommand(
scene,
command,
context,
derivedCommands.depth
);
}
derivedCommands.originalCommand = command;
if (scene._hdr) {
derivedCommands.hdr = DerivedCommand.createHdrCommand(
comman