import BoundingSphere from '../Core/BoundingSphere.js';
import buildModuleUrl from '../Core/buildModuleUrl.js';
import Cartesian3 from '../Core/Cartesian3.js';
import Cartographic from '../Core/Cartographic.js';
import defaultValue from '../Core/defaultValue.js';
import defined from '../Core/defined.js';
import defineProperties from '../Core/defineProperties.js';
import destroyObject from '../Core/destroyObject.js';
import DeveloperError from '../Core/DeveloperError.js';
import Ellipsoid from '../Core/Ellipsoid.js';
import EllipsoidTerrainProvider from '../Core/EllipsoidTerrainProvider.js';
import Event from '../Core/Event.js';
import IntersectionTests from '../Core/IntersectionTests.js';
import Ray from '../Core/Ray.js';
import Rectangle from '../Core/Rectangle.js';
import Resource from '../Core/Resource.js';
import ShaderSource from '../Renderer/ShaderSource.js';
import Texture from '../Renderer/Texture.js';
import GlobeFS from '../Shaders/GlobeFS.js';
import GlobeVS from '../Shaders/GlobeVS.js';
import GroundAtmosphere from '../Shaders/GroundAtmosphere.js';
import when from '../ThirdParty/when.js';
import GlobeSurfaceShaderSet from './GlobeSurfaceShaderSet.js';
import GlobeSurfaceTileProvider from './GlobeSurfaceTileProvider.js';
import ImageryLayerCollection from './ImageryLayerCollection.js';
import QuadtreePrimitive from './QuadtreePrimitive.js';
import SceneMode from './SceneMode.js';
import ShadowMode from './ShadowMode.js';
import TileSelectionResult from './TileSelectionResult.js';
/**
* The globe rendered in the scene, including its terrain ({@link Globe#terrainProvider})
* and imagery layers ({@link Globe#imageryLayers}). Access the globe using {@link Scene#globe}.
*
* @alias Globe
* @constructor
*
* @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] Determines the size and shape of the
* globe.
*/
function Globe(ellipsoid) {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
var terrainProvider = new EllipsoidTerrainProvider({
ellipsoid : ellipsoid
});
var imageryLayerCollection = new ImageryLayerCollection();
this._ellipsoid = ellipsoid;
this._imageryLayerCollection = imageryLayerCollection;
this._surfaceShaderSet = new GlobeSurfaceShaderSet();
this._material = undefined;
this._surface = new QuadtreePrimitive({
tileProvider : new GlobeSurfaceTileProvider({
terrainProvider : terrainProvider,
imageryLayers : imageryLayerCollection,
surfaceShaderSet : this._surfaceShaderSet
})
});
this._terrainProvider = terrainProvider;
this._terrainProviderChanged = new Event();
makeShadersDirty(this);
/**
* Determines if the globe will be shown.
*
* @type {Boolean}
* @default true
*/
this.show = true;
this._oceanNormalMapResourceDirty = true;
this._oceanNormalMapResource = new Resource({
url: buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg')
});
/**
* The maximum screen-space error used to drive level-of-detail refinement. Higher
* values will provide better performance but lower visual quality.
*
* @type {Number}
* @default 2
*/
this.maximumScreenSpaceError = 2;
/**
* The size of the terrain tile cache, expressed as a number of tiles. Any additional
* tiles beyond this number will be freed, as long as they aren't needed for rendering
* this frame. A larger number will consume more memory but will show detail faster
* when, for example, zooming out and then back in.
*
* @type {Number}
* @default 100
*/
this.tileCacheSize = 100;
/**
* Gets or sets the number of loading descendant tiles that is considered "too many".
* If a tile has too many loading descendants, that tile will be loaded and rendered before any of
* its descendants are loaded and rendered. This means more feedback for the user that something
* is happening at the cost of a longer overall load time. Setting this to 0 will cause each
* tile level to be loaded successively, significantly increasing load time. Setting it to a large
* number (e.g. 1000) will minimize the number of tiles that are loaded but tend to make
* detail appear all at once after a long wait.
* @type {Number}
* @default 20
*/
this.loadingDescendantLimit = 20;
/**
* Gets or sets a value indicating whether the ancestors of rendered tiles should be preloaded.
* Setting this to true optimizes the zoom-out experience and provides more detail in
* newly-exposed areas when panning. The down side is that it requires loading more tiles.
* @type {Boolean}
* @default true
*/
this.preloadAncestors = true;
/**
* Gets or sets a value indicating whether the siblings of rendered tiles should be preloaded.
* Setting this to true causes tiles with the same parent as a rendered tile to be loaded, even
* if they are culled. Setting this to true may provide a better panning experience at the
* cost of loading more tiles.
* @type {Boolean}
* @default false
*/
this.preloadSiblings = false;
/**
* The color to use to highlight terrain fill tiles. If undefined, fill tiles are not
* highlighted at all. The alpha value is used to alpha blend with the tile's
* actual color. Because terrain fill tiles do not represent the actual terrain surface,
* it may be useful in some applications to indicate visually that they are not to be trusted.
* @type {Color}
* @default undefined
*/
this.fillHighlightColor = undefined;
/**
* Enable lighting the globe with the sun as a light source.
*
* @type {Boolean}
* @default false
*/
this.enableLighting = false;
/**
* Enable the ground atmosphere, which is drawn over the globe when viewed from a distance between lightingFadeInDistance
and lightingFadeOutDistance
.
*
* @demo {@link https://sandcastle.cesium.com/index.html?src=Ground%20Atmosphere.html|Ground atmosphere demo in Sandcastle}
*
* @type {Boolean}
* @default true
*/
this.showGroundAtmosphere = true;
/**
* The distance where everything becomes lit. This only takes effect
* when enableLighting
or showGroundAtmosphere
is true
.
*
* @type {Number}
* @default 10000000.0
*/
this.lightingFadeOutDistance = 1.0e7;
/**
* The distance where lighting resumes. This only takes effect
* when enableLighting
or showGroundAtmosphere
is true
.
*
* @type {Number}
* @default 20000000.0
*/
this.lightingFadeInDistance = 2.0e7;
/**
* The distance where the darkness of night from the ground atmosphere fades out to a lit ground atmosphere.
* This only takes effect when showGroundAtmosphere
and enableLighting
are true
.
*
* @type {Number}
* @default 10000000.0
*/
this.nightFadeOutDistance = 1.0e7;
/**
* The distance where the darkness of night from the ground atmosphere fades in to an unlit ground atmosphere.
* This only takes effect when showGroundAtmosphere
and enableLighting
are true
.
*
* @type {Number}
* @default 50000000.0
*/
this.nightFadeInDistance = 5.0e7;
/**
* True if an animated wave effect should be shown in areas of the globe
* covered by water; otherwise, false. This property is ignored if the
* terrainProvider
does not provide a water mask.
*
* @type {Boolean}
* @default true
*/
this.showWaterEffect = true;
/**
* True if primitives such as billboards, polylines, labels, etc. should be depth-tested
* against the terrain surface, or false if such primitives should always be drawn on top
* of terrain unless they're on the opposite side of the globe. The disadvantage of depth
* testing primitives against terrain is that slight numerical noise or terrain level-of-detail
* switched can sometimes make a primitive that should be on the surface disappear underneath it.
*
* @type {Boolean}
* @default false
*
*/
this.depthTestAgainstTerrain = false;
/**
* Determines whether the globe casts or receives shadows from each light source. Setting the globe
* to cast shadows may impact performance since the terrain is rendered again from the light's perspective.
* Currently only terrain that is in view casts shadows. By default the globe does not cast shadows.
*
* @type {ShadowMode}
* @default ShadowMode.RECEIVE_ONLY
*/
this.shadows = ShadowMode.RECEIVE_ONLY;
/**
* The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A hue shift of 1.0 indicates a complete rotation of the hues available.
* @type {Number}
* @default 0.0
*/
this.atmosphereHueShift = 0.0;
/**
* The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A saturation shift of -1.0 is monochrome.
* @type {Number}
* @default 0.0
*/
this.atmosphereSaturationShift = 0.0;
/**
* The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A brightness shift of -1.0 is complete darkness, which will let space show through.
* @type {Number}
* @default 0.0
*/
this.atmosphereBrightnessShift = 0.0;
this._oceanNormalMap = undefined;
this._zoomedOutOceanSpecularIntensity = undefined;
}
defineProperties(Globe.prototype, {
/**
* Gets an ellipsoid describing the shape of this globe.
* @memberof Globe.prototype
* @type {Ellipsoid}
*/
ellipsoid : {
get : function() {
return this._ellipsoid;
}
},
/**
* Gets the collection of image layers that will be rendered on this globe.
* @memberof Globe.prototype
* @type {ImageryLayerCollection}
*/
imageryLayers : {
get : function() {
return this._imageryLayerCollection;
}
},
/**
* Gets an event that's raised when an imagery layer is added, shown, hidden, moved, or removed.
*
* @memberof Globe.prototype
* @type {Event}
* @readonly
*/
imageryLayersUpdatedEvent : {
get : function() {
return this._surface.tileProvider.imageryLayersUpdatedEvent;
}
},
/**
* Returns true
when the tile load queue is empty, false
otherwise. When the load queue is empty,
* all terrain and imagery for the current view have been loaded.
* @memberof Globe.prototype
* @type {Boolean}
* @readonly
*/
tilesLoaded: {
get: function() {
if (!defined(this._surface)) {
return true;
}
return (this._surface.tileProvider.ready && this._surface._tileLoadQueueHigh.length === 0 && this._surface._tileLoadQueueMedium.length === 0 && this._surface._tileLoadQueueLow.length === 0);
}
},
/**
* Gets or sets the color of the globe when no imagery is available.
* @memberof Globe.prototype
* @type {Color}
*/
baseColor : {
get : function() {
return this._surface.tileProvider.baseColor;
},
set : function(value) {
this._surface.tileProvider.baseColor = value;
}
},
/**
* A property specifying a {@link ClippingPlaneCollection} used to selectively disable rendering on the outside of each plane.
*
* @memberof Globe.prototype
* @type {ClippingPlaneCollection}
*/
clippingPlanes : {
get : function() {
return this._surface.tileProvider.clippingPlanes;
},
set : function(value) {
this._surface.tileProvider.clippingPlanes = value;
}
},
/**
* A property specifying a {@link Rectangle} used to limit globe rendering to a cartographic area.
* Defaults to the maximum extent of cartographic coordinates.
*
* @member Globe.prototype
* @type {Rectangle}
* @default Rectangle.MAX_VALUE
*/
cartographicLimitRectangle : {
get : function() {
return this._surface.tileProvider.cartographicLimitRectangle;
},
set : function(value) {
if (!defined(value)) {
value = Rectangle.clone(Rectangle.MAX_VALUE);
}
this._surface.tileProvider.cartographicLimitRectangle = value;
}
},
/**
* The normal map to use for rendering waves in the ocean. Setting this property will
* only have an effect if the configured terrain provider includes a water mask.
* @memberof Globe.prototype
* @type {String}
* @default buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg')
*/
oceanNormalMapUrl: {
get: function() {
return this._oceanNormalMapResource.url;
},
set: function(value) {
this._oceanNormalMapResource.url = value;
this._oceanNormalMapResourceDirty = true;
}
},
/**
* The terrain provider providing surface geometry for this globe.
* @type {TerrainProvider}
*
* @memberof Globe.prototype
* @type {TerrainProvider}
*
*/
terrainProvider : {
get : function() {
return this._terrainProvider;
},
set : function(value) {
if (value !== this._terrainProvider) {
this._terrainProvider = value;
this._terrainProviderChanged.raiseEvent(value);
if (defined(this._material)) {
makeShadersDirty(this);
}
}
}
},
/**
* Gets an event that's raised when the terrain provider is changed
*
* @memberof Globe.prototype
* @type {Event}
* @readonly
*/
terrainProviderChanged : {
get: function() {
return this._terrainProviderChanged;
}
},
/**
* Gets an event that's raised when the length of the tile load queue has changed since the last render frame. When the load queue is empty,
* all terrain and imagery for the current view have been loaded. The event passes the new length of the tile load queue.
*
* @memberof Globe.prototype
* @type {Event}
*/
tileLoadProgressEvent : {
get: function() {
return this._surface.tileLoadProgressEvent;
}
},
/**
* Gets or sets the material appearance of the Globe. This can be one of several built-in {@link Material} objects or a custom material, scripted with
* {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}.
* @memberof Globe.prototype
* @type {Material}
*/
material: {
get: function() {
return this._material;
},
set: function(material) {
if (this._material !== material) {
this._material = material;
makeShadersDirty(this);
}
}
}
});
function makeShadersDirty(globe) {
var defines = [];
var requireNormals = defined(globe._material) && (globe._material.shaderSource.match(/slope/) || globe._material.shaderSource.match('normalEC'));
var fragmentSources = [GroundAtmosphere];
if (defined(globe._material) && (!requireNormals || globe._terrainProvider.requestVertexNormals)) {
fragmentSources.push(globe._material.shaderSource);
defines.push('APPLY_MATERIAL');
globe._surface._tileProvider.uniformMap = globe._material._uniforms;
} else {
globe._surface._tileProvider.uniformMap = undefined;
}
fragmentSources.push(GlobeFS);
globe._surfaceShaderSet.baseVertexShaderSource = new ShaderSource({
sources : [GroundAtmosphere, GlobeVS],
defines : defines
});
globe._surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
sources : fragmentSources,
defines : defines
});
globe._surfaceShaderSet.material = globe._material;
}
function createComparePickTileFunction(rayOrigin) {
return function(a, b) {
var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin);
var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin);
return aDist - bDist;
};
}
var scratchArray = [];
var scratchSphereIntersectionResult = {
start : 0.0,
stop : 0.0
};
/**
* Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates.
*
* @param {Ray} ray The ray to test for intersection.
* @param {Scene} scene The scene.
* @param {Cartesian3} [result] The object onto which to store the result.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found. The returned position is in projected coordinates for 2D and Columbus View.
*
* @private
*/
Globe.prototype.pickWorldCoordinates = function(ray, scene, result) {
//>>includeStart('debug', pragmas.debug);
if (!defined(ray)) {
throw new DeveloperError('ray is required');
}
if (!defined(scene)) {
throw new DeveloperError('scene is required');
}
//>>includeEnd('debug');
var mode = scene.mode;
var projection = scene.mapProjection;
var sphereIntersections = scratchArray;
sphereIntersections.length = 0;
var tilesToRender = this._surface._tilesToRender;
var length = tilesToRender.length;
var tile;
var i;
for (i = 0; i < length; ++i) {
tile = tilesToRender[i];
var surfaceTile = tile.data;
if (!defined(surfaceTile)) {
continue;
}
var boundingVolume = surfaceTile.pickBoundingSphere;
if (mode !== SceneMode.SCENE3D) {
surfaceTile.pickBoundingSphere = boundingVolume = BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, surfaceTile.tileBoundingRegion.minimumHeight, surfaceTile.tileBoundingRegion.maximumHeight, boundingVolume);
Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center);
} else if (defined(surfaceTile.renderedMesh)) {
BoundingSphere.clone(surfaceTile.renderedMesh.boundingSphere3D, boundingVolume);
} else {
// So wait how did we render this thing then? It shouldn't be possible to get here.
continue;
}
var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult);
if (defined(boundingSphereIntersection)) {
sphereIntersections.push(surfaceTile);
}
}
sphereIntersections.sort(createComparePickTileFunction(ray.origin));
var intersection;
length = sphereIntersections.length;
for (i = 0; i < length; ++i) {
intersection = sphereIntersections[i].pick(ray, scene.mode, scene.mapProjection, true, result);
if (defined(intersection)) {
break;
}
}
return intersection;
};
var cartoScratch = new Cartographic();
/**
* Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates.
*
* @param {Ray} ray The ray to test for intersection.
* @param {Scene} scene The scene.
* @param {Cartesian3} [result] The object onto which to store the result.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @example
* // find intersection of ray through a pixel and the globe
* var ray = viewer.camera.getPickRay(windowCoordinates);
* var intersection = globe.pick(ray, scene);
*/
Globe.prototype.pick = function(ray, scene, result) {
result = this.pickWorldCoordinates(ray, scene, result);
if (defined(result) && scene.mode !== SceneMode.SCENE3D) {
result = Cartesian3.fromElements(result.y, result.z, result.x, result);
var carto = scene.mapProjection.unproject(result, cartoScratch);
result = scene.globe.ellipsoid.cartographicToCartesian(carto, result);
}
return result;
};
var scratchGetHeightCartesian = new Cartesian3();
var scratchGetHeightIntersection = new Cartesian3();
var scratchGetHeightCartographic = new Cartographic();
var scratchGetHeightRay = new Ray();
function tileIfContainsCartographic(tile, cartographic) {
return Rectangle.contains(tile.rectangle, cartographic) ? tile : undefined;
}
/**
* Get the height of the surface at a given cartographic.
*
* @param {Cartographic} cartographic The cartographic for which to find the height.
* @returns {Number|undefined} The height of the cartographic or undefined if it could not be found.
*/
Globe.prototype.getHeight = function(cartographic) {
//>>includeStart('debug', pragmas.debug);
if (!defined(cartographic)) {
throw new DeveloperError('cartographic is required');
}
//>>includeEnd('debug');
var levelZeroTiles = this._surface._levelZeroTiles;
if (!defined(levelZeroTiles)) {
return;
}
var tile;
var i;
var length = levelZeroTiles.length;
for (i = 0; i < length; ++i) {
tile = levelZeroTiles[i];
if (Rectangle.contains(tile.rectangle, cartographic)) {
break;
}
}
if (i >= length) {
return undefined;
}
while (tile._lastSelectionResult === TileSelectionResult.REFINED) {
tile = tileIfContainsCartographic(tile.southwestChild, cartographic) ||
tileIfContainsCartographic(tile.southeastChild, cartographic) ||
tileIfContainsCartographic(tile.northwestChild, cartographic) ||
tile.northeastChild;
}
// This tile was either rendered or culled.
// It is sometimes useful to get a height from a culled tile,
// e.g. when we're getting a height in order to place a billboard
// on terrain, and the camera is looking at that same billboard.
// The culled tile must have a valid mesh, though.
if (!defined(tile.data) || !defined(tile.data.renderedMesh)) {
// Tile was not rendered (culled).
return undefined;
}
var ellipsoid = this._surface._tileProvider.tilingScheme.ellipsoid;
//cartesian has to be on the ellipsoid surface for `ellipsoid.geodeticSurfaceNormal`
var cartesian = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0, ellipsoid, scratchGetHeightCartesian);
var ray = scratchGetHeightRay;
var surfaceNormal = ellipsoid.geodeticSurfaceNormal(cartesian, ray.direction);
// Try to find the intersection point between the surface normal and z-axis.
// minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
var rayOrigin = ellipsoid.getSurfaceNormalIntersectionWithZAxis(cartesian, 11500.0, ray.origin);
// Theoretically, not with Earth datums, the intersection point can be outside the ellipsoid
if (!defined(rayOrigin)) {
// intersection point is outside the ellipsoid, try other value
// minimum height (-11500.0) for the terrain set, need to get this information from the terrain provider
var minimumHeight;
if (defined(tile.data.tileBoundingRegion)) {
minimumHeight = tile.data.tileBoundingRegion.minimumHeight;
}
var magnitude = Math.min(defaultValue(minimumHeight, 0.0), -11500.0);
// multiply by the *positive* value of the magnitude
var vectorToMinimumPoint = Cartesian3.multiplyByScalar(surfaceNormal, Math.abs(magnitude) + 1, scratchGetHeightIntersection);
Cartesian3.subtract(cartesian, vectorToMinimumPoint, ray.origin);
}
var intersection = tile.data.pick(ray, undefined, undefined, false, scratchGetHeightIntersection);
if (!defined(intersection)) {
return undefined;
}
return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height;
};
/**
* @private
*/
Globe.prototype.update = function(frameState) {
if (!this.show) {
return;
}
if (frameState.passes.render) {
this._surface.update(frameState);
}
};
/**
* @private
*/
Globe.prototype.beginFrame = function(frameState) {
var surface = this._surface;
var tileProvider = surface.tileProvider;
var terrainProvider = this.terrainProvider;
var hasWaterMask = this.showWaterEffect && terrainProvider.ready && terrainProvider.hasWaterMask;
if (hasWaterMask && this._oceanNormalMapResourceDirty) {
// url changed, load new normal map asynchronously
this._oceanNormalMapResourceDirty = false;
var oceanNormalMapResource = this._oceanNormalMapResource;
var oceanNormalMapUrl = oceanNormalMapResource.url;
if (defined(oceanNormalMapUrl)) {
var that = this;
when(oceanNormalMapResource.fetchImage(), function(image) {
if (oceanNormalMapUrl !== that._oceanNormalMapResource.url) {
// url changed while we were loading
return;
}
that._oceanNormalMap = that._oceanNormalMap && that._oceanNormalMap.destroy();
that._oceanNormalMap = new Texture({
context : frameState.context,
source : image
});
});
} else {
this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy();
}
}
var pass = frameState.passes;
var mode = frameState.mode;
if (pass.render) {
if (this.showGroundAtmosphere) {
this._zoomedOutOceanSpecularIntensity = 0.4;
} else {
this._zoomedOutOceanSpecularIntensity = 0.5;
}
surface.maximumScreenSpaceError = this.maximumScreenSpaceError;
surface.tileCacheSize = this.tileCacheSize;
surface.loadingDescendantLimit = this.loadingDescendantLimit;
surface.preloadAncestors = this.preloadAncestors;
surface.preloadSiblings = this.preloadSiblings;
tileProvider.terrainProvider = this.terrainProvider;
tileProvider.lightingFadeOutDistance = this.lightingFadeOutDistance;
tileProvider.lightingFadeInDistance = this.lightingFadeInDistance;
tileProvider.nightFadeOutDistance = this.nightFadeOutDistance;
tileProvider.nightFadeInDistance = this.nightFadeInDistance;
tileProvider.zoomedOutOceanSpecularIntensity = mode === SceneMode.SCENE3D ? this._zoomedOutOceanSpecularIntensity : 0.0;
tileProvider.hasWaterMask = hasWaterMask;
tileProvider.oceanNormalMap = this._oceanNormalMap;
tileProvider.enableLighting = this.enableLighting;
tileProvider.showGroundAtmosphere = this.showGroundAtmosphere;
tileProvider.shadows = this.shadows;
tileProvider.hueShift = this.atmosphereHueShift;
tileProvider.saturationShift = this.atmosphereSaturationShift;
tileProvider.brightnessShift = this.atmosphereBrightnessShift;
tileProvider.fillHighlightColor = this.fillHighlightColor;
surface.beginFrame(frameState);
}
};
/**
* @private
*/
Globe.prototype.render = function(frameState) {
if (!this.show) {
return;
}
if (defined(this._material)) {
this._material.update(frameState.context);
}
this._surface.render(frameState);
};
/**
* @private
*/
Globe.prototype.endFrame = function(frameState) {
if (!this.show) {
return;
}
if (frameState.passes.render) {
this._surface.endFrame(frameState);
}
};
/**
* Returns true if this object was destroyed; otherwise, false.
*
* If this object was destroyed, it should not be used; calling any function other than
* isDestroyed
will result in a {@link DeveloperError} exception.
*
* @returns {Boolean} True if this object was destroyed; otherwise, false.
*
* @see Globe#destroy
*/
Globe.prototype.isDestroyed = function() {
return false;
};
/**
* Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
* release of WebGL resources, instead of relying on the garbage collector to destroy this object.
*
* Once an object is destroyed, it should not be used; calling any function other than
* isDestroyed
will result in a {@link DeveloperError} exception. Therefore,
* assign the return value (undefined
) to the object as done in the example.
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
*
*
* @example
* globe = globe && globe.destroy();
*
* @see Globe#isDestroyed
*/
Globe.prototype.destroy = function() {
this._surfaceShaderSet = this._surfaceShaderSet && this._surfaceShaderSet.destroy();
this._surface = this._surface && this._surface.destroy();
this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy();
return destroyObject(this);
};
export default Globe;