cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
1,141 lines (1,051 loc) • 84.5 kB
JavaScript
define([
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartographic',
'../Core/Check',
'../Core/defaultValue',
'../Core/defined',
'../Core/defineProperties',
'../Core/deprecationWarning',
'../Core/destroyObject',
'../Core/DeveloperError',
'../Core/DoublyLinkedList',
'../Core/Ellipsoid',
'../Core/Event',
'../Core/getMagic',
'../Core/JulianDate',
'../Core/ManagedArray',
'../Core/Math',
'../Core/Matrix4',
'../Core/Resource',
'../Core/RuntimeError',
'../Renderer/ClearCommand',
'../Renderer/Pass',
'../ThirdParty/when',
'./Axis',
'./Cesium3DTile',
'./Cesium3DTileColorBlendMode',
'./Cesium3DTileContentState',
'./Cesium3DTileOptimizations',
'./Cesium3DTilesetStatistics',
'./Cesium3DTilesetTraversal',
'./Cesium3DTileStyleEngine',
'./ClippingPlaneCollection',
'./LabelCollection',
'./PointCloudEyeDomeLighting',
'./PointCloudShading',
'./SceneMode',
'./ShadowMode',
'./TileBoundingRegion',
'./TileBoundingSphere',
'./TileOrientedBoundingBox'
], function(
Cartesian2,
Cartesian3,
Cartographic,
Check,
defaultValue,
defined,
defineProperties,
deprecationWarning,
destroyObject,
DeveloperError,
DoublyLinkedList,
Ellipsoid,
Event,
getMagic,
JulianDate,
ManagedArray,
CesiumMath,
Matrix4,
Resource,
RuntimeError,
ClearCommand,
Pass,
when,
Axis,
Cesium3DTile,
Cesium3DTileColorBlendMode,
Cesium3DTileContentState,
Cesium3DTileOptimizations,
Cesium3DTilesetStatistics,
Cesium3DTilesetTraversal,
Cesium3DTileStyleEngine,
ClippingPlaneCollection,
LabelCollection,
PointCloudEyeDomeLighting,
PointCloudShading,
SceneMode,
ShadowMode,
TileBoundingRegion,
TileBoundingSphere,
TileOrientedBoundingBox) {
'use strict';
/**
* A {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles tileset},
* used for streaming massive heterogeneous 3D geospatial datasets.
*
* @alias Cesium3DTileset
* @constructor
*
* @param {Object} options Object with the following properties:
* @param {Resource|String|Promise<Resource>|Promise<String>} options.url The url to a tileset.json file or to a directory containing a tileset.json file.
* @param {Boolean} [options.show=true] Determines if the tileset will be shown.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the tileset's root tile.
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source.
* @param {Number} [options.maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement.
* @param {Number} [options.maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset.
* @param {Boolean} [options.cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes.
* @param {Boolean} [options.dynamicScreenSpaceError=false] Optimization option. Reduce the screen space error for tiles that are further away from the camera.
* @param {Number} [options.dynamicScreenSpaceErrorDensity=0.00278] Density used to adjust the dynamic screen space error, similar to fog density.
* @param {Number} [options.dynamicScreenSpaceErrorFactor=4.0] A factor used to increase the computed dynamic screen space error.
* @param {Number} [options.dynamicScreenSpaceErrorHeightFalloff=0.25] A ratio of the tileset's height at which the density starts to falloff.
* @param {Boolean} [options.skipLevelOfDetail=true] Optimization option. Determines if level of detail skipping should be applied during the traversal.
* @param {Number} [options.baseScreenSpaceError=1024] When <code>skipLevelOfDetail</code> is <code>true</code>, the screen space error that must be reached before skipping levels of detail.
* @param {Number} [options.skipScreenSpaceErrorFactor=16] When <code>skipLevelOfDetail</code> is <code>true</code>, a multiplier defining the minimum screen space error to skip. Used in conjunction with <code>skipLevels</code> to determine which tiles to load.
* @param {Number} [options.skipLevels=1] When <code>skipLevelOfDetail</code> is <code>true</code>, a constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjunction with <code>skipScreenSpaceErrorFactor</code> to determine which tiles to load.
* @param {Boolean} [options.immediatelyLoadDesiredLevelOfDetail=false] When <code>skipLevelOfDetail</code> is <code>true</code>, only tiles that meet the maximum screen space error will ever be downloaded. Skipping factors are ignored and just the desired tiles are loaded.
* @param {Boolean} [options.loadSiblings=false] When <code>skipLevelOfDetail</code> is <code>true</code>, determines whether siblings of visible tiles are always downloaded during traversal.
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset.
* @param {ClassificationType} [options.classificationType] Determines whether terrain, 3D Tiles or both will be classified by this tileset. See {@link Cesium3DTileset#classificationType} for details about restrictions and limitations.
* @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid determining the size and shape of the globe.
* @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering.
* @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile.
* @param {Boolean} [options.debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile.
* @param {Boolean} [options.debugShowContentBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile's content.
* @param {Boolean} [options.debugShowViewerRequestVolume=false] For debugging only. When true, renders the viewer request volume for each tile.
* @param {Boolean} [options.debugShowGeometricError=false] For debugging only. When true, draws labels to indicate the geometric error of each tile.
* @param {Boolean} [options.debugShowRenderingStatistics=false] For debugging only. When true, draws labels to indicate the number of commands, points, triangles and features for each tile.
* @param {Boolean} [options.debugShowMemoryUsage=false] For debugging only. When true, draws labels to indicate the texture and geometry memory in megabytes used by each tile.
* @param {Boolean} [options.debugShowUrl=false] For debugging only. When true, draws labels to indicate the url of each tile.
* @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
*
* @exception {DeveloperError} The tileset must be 3D Tiles version 0.0 or 1.0. See {@link https://github.com/AnalyticalGraphicsInc/3d-tiles#spec-status}
*
* @example
* var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({
* url : 'http://localhost:8002/tilesets/Seattle'
* }));
*
* @example
* // Common setting for the skipLevelOfDetail optimization
* var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({
* url : 'http://localhost:8002/tilesets/Seattle',
* skipLevelOfDetail : true,
* baseScreenSpaceError : 1024,
* skipScreenSpaceErrorFactor : 16,
* skipLevels : 1,
* immediatelyLoadDesiredLevelOfDetail : false,
* loadSiblings : false,
* cullWithChildrenBounds : true
* }));
*
* @example
* // Common settings for the dynamicScreenSpaceError optimization
* var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({
* url : 'http://localhost:8002/tilesets/Seattle',
* dynamicScreenSpaceError : true,
* dynamicScreenSpaceErrorDensity : 0.00278,
* dynamicScreenSpaceErrorFactor : 4.0,
* dynamicScreenSpaceErrorHeightFalloff : 0.25
* }));
*
* @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles specification}
*/
function Cesium3DTileset(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
Check.defined('options.url', options.url);
//>>includeEnd('debug');
this._url = undefined;
this._tilesetUrl = undefined;
this._basePath = undefined;
this._root = undefined;
this._asset = undefined; // Metadata for the entire tileset
this._properties = undefined; // Metadata for per-model/point/etc properties
this._geometricError = undefined; // Geometric error when the tree is not rendered at all
this._gltfUpAxis = undefined;
this._processingQueue = [];
this._selectedTiles = [];
this._requestedTiles = [];
this._desiredTiles = new ManagedArray();
this._selectedTilesToStyle = [];
this._loadTimestamp = undefined;
this._timeSinceLoad = 0.0;
var replacementList = new DoublyLinkedList();
// [head, sentinel) -> tiles that weren't selected this frame and may be replaced
// (sentinel, tail] -> tiles that were selected this frame
this._replacementList = replacementList; // Tiles with content loaded. For cache management.
this._replacementSentinel = replacementList.add();
this._trimTiles = false;
this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true);
this._hasMixedContent = false;
this._baseTraversal = new Cesium3DTilesetTraversal.BaseTraversal();
this._skipTraversal = new Cesium3DTilesetTraversal.SkipTraversal({
selectionHeuristic : selectionHeuristic
});
this._backfaceCommands = new ManagedArray();
this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16);
this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512);
this._styleEngine = new Cesium3DTileStyleEngine();
this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY);
this._statistics = new Cesium3DTilesetStatistics();
this._statisticsLastColor = new Cesium3DTilesetStatistics();
this._statisticsLastPick = new Cesium3DTilesetStatistics();
this._tilesLoaded = false;
this._tileDebugLabels = undefined;
this._readyPromise = when.defer();
this._classificationType = options.classificationType;
this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
/**
* Optimization option. Whether the tileset should refine based on a dynamic screen space error. Tiles that are further
* away will be rendered with lower detail than closer tiles. This improves performance by rendering fewer
* tiles and making less requests, but may result in a slight drop in visual quality for tiles in the distance.
* The algorithm is biased towards "street views" where the camera is close to the ground plane of the tileset and looking
* at the horizon. In addition results are more accurate for tightly fitting bounding volumes like box and region.
*
* @type {Boolean}
* @default false
*/
this.dynamicScreenSpaceError = defaultValue(options.dynamicScreenSpaceError, false);
/**
* A scalar that determines the density used to adjust the dynamic screen space error, similar to {@link Fog}. Increasing this
* value has the effect of increasing the maximum screen space error for all tiles, but in a non-linear fashion.
* The error starts at 0.0 and increases exponentially until a midpoint is reached, and then approaches 1.0 asymptotically.
* This has the effect of keeping high detail in the closer tiles and lower detail in the further tiles, with all tiles
* beyond a certain distance all roughly having an error of 1.0.
* <p>
* The dynamic error is in the range [0.0, 1.0) and is multiplied by <code>dynamicScreenSpaceErrorFactor</code> to produce the
* final dynamic error. This dynamic error is then subtracted from the tile's actual screen space error.
* </p>
* <p>
* Increasing <code>dynamicScreenSpaceErrorDensity</code> has the effect of moving the error midpoint closer to the camera.
* It is analogous to moving fog closer to the camera.
* </p>
*
* @type {Number}
* @default 0.00278
*/
this.dynamicScreenSpaceErrorDensity = 0.00278;
/**
* A factor used to increase the screen space error of tiles for dynamic screen space error. As this value increases less tiles
* are requested for rendering and tiles in the distance will have lower detail. If set to zero, the feature will be disabled.
*
* @type {Number}
* @default 4.0
*/
this.dynamicScreenSpaceErrorFactor = 4.0;
/**
* A ratio of the tileset's height at which the density starts to falloff. If the camera is below this height the
* full computed density is applied, otherwise the density falls off. This has the effect of higher density at
* street level views.
* <p>
* Valid values are between 0.0 and 1.0.
* </p>
*
* @type {Number}
* @default 0.25
*/
this.dynamicScreenSpaceErrorHeightFalloff = 0.25;
this._dynamicScreenSpaceErrorComputedDensity = 0.0; // Updated based on the camera position and direction
/**
* Determines whether the tileset casts or receives shadows from each light source.
* <p>
* Enabling shadows has a performance impact. A tileset that casts shadows must be rendered twice, once from the camera and again from the light's point of view.
* </p>
* <p>
* Shadows are rendered only when {@link Viewer#shadows} is <code>true</code>.
* </p>
*
* @type {ShadowMode}
* @default ShadowMode.ENABLED
*/
this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
/**
* Determines if the tileset will be shown.
*
* @type {Boolean}
* @default true
*/
this.show = defaultValue(options.show, true);
/**
* Defines how per-feature colors set from the Cesium API or declarative styling blend with the source colors from
* the original feature, e.g. glTF material or per-point color in the tile.
*
* @type {Cesium3DTileColorBlendMode}
* @default Cesium3DTileColorBlendMode.HIGHLIGHT
*/
this.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT;
/**
* Defines the value used to linearly interpolate between the source color and feature color when the {@link Cesium3DTileset#colorBlendMode} is <code>MIX</code>.
* A value of 0.0 results in the source color while a value of 1.0 results in the feature color, with any value in-between
* resulting in a mix of the source color and feature color.
*
* @type {Number}
* @default 0.5
*/
this.colorBlendAmount = 0.5;
/**
* Options for controlling point size based on geometric error and eye dome lighting.
* @type {PointCloudShading}
*/
this.pointCloudShading = new PointCloudShading(options.pointCloudShading);
this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting();
/**
* The event fired to indicate progress of loading new tiles. This event is fired when a new tile
* is requested, when a requested tile is finished downloading, and when a downloaded tile has been
* processed and is ready to render.
* <p>
* The number of pending tile requests, <code>numberOfPendingRequests</code>, and number of tiles
* processing, <code>numberOfTilesProcessing</code> are passed to the event listener.
* </p>
* <p>
* This event is fired at the end of the frame after the scene is rendered.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.loadProgress.addEventListener(function(numberOfPendingRequests, numberOfTilesProcessing) {
* if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) {
* console.log('Stopped loading');
* return;
* }
*
* console.log('Loading: requests: ' + numberOfPendingRequests + ', processing: ' + numberOfTilesProcessing);
* });
*/
this.loadProgress = new Event();
/**
* The event fired to indicate that all tiles that meet the screen space error this frame are loaded. The tileset
* is completely loaded for this view.
* <p>
* This event is fired at the end of the frame after the scene is rendered.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.allTilesLoaded.addEventListener(function() {
* console.log('All tiles are loaded');
* });
*
* @see Cesium3DTileset#tilesLoaded
*/
this.allTilesLoaded = new Event();
/**
* The event fired to indicate that a tile's content was loaded.
* <p>
* The loaded {@link Cesium3DTile} is passed to the event listener.
* </p>
* <p>
* This event is fired during the tileset traversal while the frame is being rendered
* so that updates to the tile take effect in the same frame. Do not create or modify
* Cesium entities or primitives during the event listener.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.tileLoad.addEventListener(function(tile) {
* console.log('A tile was loaded.');
* });
*/
this.tileLoad = new Event();
/**
* The event fired to indicate that a tile's content was unloaded.
* <p>
* The unloaded {@link Cesium3DTile} is passed to the event listener.
* </p>
* <p>
* This event is fired immediately before the tile's content is unloaded while the frame is being
* rendered so that the event listener has access to the tile's content. Do not create
* or modify Cesium entities or primitives during the event listener.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.tileUnload.addEventListener(function(tile) {
* console.log('A tile was unloaded from the cache.');
* });
*
* @see Cesium3DTileset#maximumMemoryUsage
* @see Cesium3DTileset#trimLoadedTiles
*/
this.tileUnload = new Event();
/**
* The event fired to indicate that a tile's content failed to load.
* <p>
* If there are no event listeners, error messages will be logged to the console.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.tileFailed.addEventListener(function(error) {
* console.log('An error occurred loading tile: ' + error.url);
* console.log('Error: ' + error.message);
* });
*/
this.tileFailed = new Event();
/**
* This event fires once for each visible tile in a frame. This can be used to manually
* style a tileset.
* <p>
* The visible {@link Cesium3DTile} is passed to the event listener.
* </p>
* <p>
* This event is fired during the tileset traversal while the frame is being rendered
* so that updates to the tile take effect in the same frame. Do not create or modify
* Cesium entities or primitives during the event listener.
* </p>
*
* @type {Event}
* @default new Event()
*
* @example
* tileset.tileVisible.addEventListener(function(tile) {
* if (tile.content instanceof Cesium.Batched3DModel3DTileContent) {
* console.log('A Batched 3D Model tile is visible.');
* }
* });
*
* @example
* // Apply a red style and then manually set random colors for every other feature when the tile becomes visible.
* tileset.style = new Cesium.Cesium3DTileStyle({
* color : 'color("red")'
* });
* tileset.tileVisible.addEventListener(function(tile) {
* var content = tile.content;
* var featuresLength = content.featuresLength;
* for (var i = 0; i < featuresLength; i+=2) {
* content.getFeature(i).color = Cesium.Color.fromRandom();
* }
* });
*/
this.tileVisible = new Event();
/**
* Optimization option. Determines if level of detail skipping should be applied during the traversal.
* <p>
* The common strategy for replacement-refinement traversal is to store all levels of the tree in memory and require
* all children to be loaded before the parent can refine. With this optimization levels of the tree can be skipped
* entirely and children can be rendered alongside their parents. The tileset requires significantly less memory when
* using this optimization.
* </p>
*
* @type {Boolean}
* @default true
*/
this.skipLevelOfDetail = defaultValue(options.skipLevelOfDetail, true);
this._skipLevelOfDetail = this.skipLevelOfDetail;
this._disableSkipLevelOfDetail = false;
/**
* The screen space error that must be reached before skipping levels of detail.
* <p>
* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is <code>true</code>.
* </p>
*
* @type {Number}
* @default 1024
*/
this.baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 1024);
/**
* Multiplier defining the minimum screen space error to skip.
* For example, if a tile has screen space error of 100, no tiles will be loaded unless they
* are leaves or have a screen space error <code><= 100 / skipScreenSpaceErrorFactor</code>.
* <p>
* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is <code>true</code>.
* </p>
*
* @type {Number}
* @default 16
*/
this.skipScreenSpaceErrorFactor = defaultValue(options.skipScreenSpaceErrorFactor, 16);
/**
* Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped.
* For example, if a tile is level 1, no tiles will be loaded unless they are at level greater than 2.
* <p>
* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is <code>true</code>.
* </p>
*
* @type {Number}
* @default 1
*/
this.skipLevels = defaultValue(options.skipLevels, 1);
/**
* When true, only tiles that meet the maximum screen space error will ever be downloaded.
* Skipping factors are ignored and just the desired tiles are loaded.
* <p>
* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is <code>true</code>.
* </p>
*
* @type {Boolean}
* @default false
*/
this.immediatelyLoadDesiredLevelOfDetail = defaultValue(options.immediatelyLoadDesiredLevelOfDetail, false);
/**
* Determines whether siblings of visible tiles are always downloaded during traversal.
* This may be useful for ensuring that tiles are already available when the viewer turns left/right.
* <p>
* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is <code>true</code>.
* </p>
*
* @type {Boolean}
* @default false
*/
this.loadSiblings = defaultValue(options.loadSiblings, false);
this._clippingPlanes = undefined;
this.clippingPlanes = options.clippingPlanes;
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* Determines if only the tiles from last frame should be used for rendering. This
* effectively "freezes" the tileset to the previous frame so it is possible to zoom
* out and see what was rendered.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugFreezeFrame = defaultValue(options.debugFreezeFrame, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, assigns a random color to each tile. This is useful for visualizing
* what features belong to what tiles, especially with additive refinement where features
* from parent tiles may be interleaved with features from child tiles.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugColorizeTiles = defaultValue(options.debugColorizeTiles, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, renders each tile's content as a wireframe.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugWireframe = defaultValue(options.debugWireframe, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, renders the bounding volume for each visible tile. The bounding volume is
* white if the tile has a content bounding volume; otherwise, it is red. Tiles that don't meet the
* screen space error and are still refining to their descendants are yellow.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, renders the bounding volume for each visible tile's content. The bounding volume is
* blue if the tile has a content bounding volume; otherwise it is red.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowContentBoundingVolume = defaultValue(options.debugShowContentBoundingVolume, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, renders the viewer request volume for each tile.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowViewerRequestVolume = defaultValue(options.debugShowViewerRequestVolume, false);
this._tileDebugLabels = undefined;
this.debugPickedTileLabelOnly = false;
this.debugPickedTile = undefined;
this.debugPickPosition = undefined;
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, draws labels to indicate the geometric error of each tile.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowGeometricError = defaultValue(options.debugShowGeometricError, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, draws labels to indicate the number of commands, points, triangles and features of each tile.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowRenderingStatistics = defaultValue(options.debugShowRenderingStatistics, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, draws labels to indicate the geometry and texture memory usage of each tile.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowMemoryUsage = defaultValue(options.debugShowMemoryUsage, false);
/**
* This property is for debugging only; it is not optimized for production use.
* <p>
* When true, draws labels to indicate the url of each tile.
* </p>
*
* @type {Boolean}
* @default false
*/
this.debugShowUrl = defaultValue(options.debugShowUrl, false);
// A bunch of tilesets were generated that have a leading / in front of all URLs in the tileset.json. If the tiles aren't
// at the root of the domain they will not load anymore. If we find a b3dm file with a leading slash, we test load a tile.
// If it succeeds we continue on. If it fails, we set this to true so we know to strip the slash when loading tiles.
this._brokenUrlWorkaround = false;
this._credits = undefined;
var that = this;
var tilesetResource;
when(options.url)
.then(function(url) {
var basePath;
var resource = Resource.createIfNeeded(url);
// ion resources have a credits property we can use for additional attribution.
that._credits = resource.credits;
tilesetResource = resource;
if (resource.extension === 'json') {
basePath = resource.getBaseUri(true);
} else if (resource.isDataUri) {
basePath = '';
} else {
resource.appendForwardSlash();
tilesetResource = resource.getDerivedResource({
url: 'tileset.json'
});
basePath = resource.url;
}
that._url = resource.url;
that._tilesetUrl = tilesetResource.url;
that._basePath = basePath;
// We don't know the distance of the tileset until tileset.json is loaded, so use the default distance for now
return Cesium3DTileset.loadJson(tilesetResource);
})
.then(function(tilesetJson) {
return detectBrokenUrlWorkaround(that, tilesetResource, tilesetJson);
})
.then(function(tilesetJson) {
if (that._brokenUrlWorkaround) {
deprecationWarning('Cesium3DTileset.leadingSlash', 'Having a leading slash in a tile URL that is actually relative to the tileset.json is deprecated.');
}
that._root = that.loadTileset(tilesetResource, tilesetJson);
var gltfUpAxis = defined(tilesetJson.asset.gltfUpAxis) ? Axis.fromName(tilesetJson.asset.gltfUpAxis) : Axis.Y;
that._asset = tilesetJson.asset;
that._properties = tilesetJson.properties;
that._geometricError = tilesetJson.geometricError;
that._gltfUpAxis = gltfUpAxis;
that._readyPromise.resolve(that);
}).otherwise(function(error) {
that._readyPromise.reject(error);
});
}
function detectBrokenUrlWorkaround(tileset, tilesetResource, tilesetJson) {
var testUrl = findBrokenUrl(tilesetJson.root);
// If it's an empty string, we are good to load the tileset.
if (!defined(testUrl) || (testUrl.length === 0)) {
return tilesetJson;
}
var testResource = tilesetResource.getDerivedResource({
url : testUrl
});
return testResource.fetchArrayBuffer()
.then(function(buffer) {
var uint8Array = new Uint8Array(buffer);
var magic = getMagic(uint8Array);
// If its not a b3dm file, then use workaround
// This accounts for servers that return an error page with a 200 status code
tileset._brokenUrlWorkaround = (magic !== 'b3dm');
return tilesetJson;
})
.otherwise(function() {
// Tile failed to load, so use the workaround
tileset._brokenUrlWorkaround = true;
return tilesetJson;
});
}
var brokenUrlRegex = /^\/.+\.b3dm$/;
function findBrokenUrl(node) {
var content = node.content;
if (defined(content) && defined(content.url)) {
if (brokenUrlRegex.test(content.url)) {
return content.url;
}
return '';
}
var children = node.children;
if (defined(children)) {
var count = children.length;
for (var i = 0; i < count; ++i) {
var result = findBrokenUrl(children[i]);
if (defined(result)) {
return result;
}
}
}
}
defineProperties(Cesium3DTileset.prototype, {
/**
* Gets the tileset's asset object property, which contains metadata about the tileset.
* <p>
* See the {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/schema/asset.schema.json|asset schema}
* in the 3D Tiles spec for the full set of properties.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Object}
* @readonly
*
* @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.
*/
asset : {
get : function() {
//>>includeStart('debug', pragmas.debug);
if (!this.ready) {
throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.');
}
//>>includeEnd('debug');
return this._asset;
}
},
/**
* The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset.
*
* @type {ClippingPlaneCollection}
*/
clippingPlanes : {
get : function() {
return this._clippingPlanes;
},
set : function(value) {
ClippingPlaneCollection.setOwner(value, this, '_clippingPlanes');
}
},
/**
* Gets the tileset's properties dictionary object, which contains metadata about per-feature properties.
* <p>
* See the {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/schema/properties.schema.json|properties schema}
* in the 3D Tiles spec for the full set of properties.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Object}
* @readonly
*
* @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.
*
* @example
* console.log('Maximum building height: ' + tileset.properties.height.maximum);
* console.log('Minimum building height: ' + tileset.properties.height.minimum);
*
* @see Cesium3DTileFeature#getProperty
* @see Cesium3DTileFeature#setProperty
*/
properties : {
get : function() {
//>>includeStart('debug', pragmas.debug);
if (!this.ready) {
throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.');
}
//>>includeEnd('debug');
return this._properties;
}
},
/**
* When <code>true</code>, the tileset's root tile is loaded and the tileset is ready to render.
* This is set to <code>true</code> right before {@link Cesium3DTileset#readyPromise} is resolved.
*
* @memberof Cesium3DTileset.prototype
*
* @type {Boolean}
* @readonly
*
* @default false
*/
ready : {
get : function() {
return defined(this._root);
}
},
/**
* Gets the promise that will be resolved when the tileset's root tile is loaded and the tileset is ready to render.
* <p>
* This promise is resolved at the end of the frame before the first frame the tileset is rendered in.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Promise.<Cesium3DTileset>}
* @readonly
*
* @example
* tileset.readyPromise.then(function(tileset) {
* // tile.properties is not defined until readyPromise resolves.
* var properties = tileset.properties;
* if (Cesium.defined(properties)) {
* for (var name in properties) {
* console.log(properties[name]);
* }
* }
* });
*/
readyPromise : {
get : function() {
return this._readyPromise.promise;
}
},
/**
* When <code>true</code>, all tiles that meet the screen space error this frame are loaded. The tileset is
* completely loaded for this view.
*
* @memberof Cesium3DTileset.prototype
*
* @type {Boolean}
* @readonly
*
* @default false
*
* @see Cesium3DTileset#allTilesLoaded
*/
tilesLoaded : {
get : function() {
return this._tilesLoaded;
}
},
/**
* The url to a tileset.json file or to a directory containing a tileset.json file.
*
* @memberof Cesium3DTileset.prototype
*
* @type {String}
* @readonly
*/
url : {
get : function() {
return this._url;
}
},
/**
* The base path that non-absolute paths in tileset.json are relative to.
*
* @memberof Cesium3DTileset.prototype
*
* @type {String}
* @readonly
* @deprecated
*/
basePath : {
get : function() {
deprecationWarning('Cesium3DTileset.basePath', 'Cesium3DTileset.basePath has been deprecated. All tiles are relative to the url of the tileset.json that contains them. Use the url property instead.');
return this._basePath;
}
},
/**
* The style, defined using the
* {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language},
* applied to each feature in the tileset.
* <p>
* Assign <code>undefined</code> to remove the style, which will restore the visual
* appearance of the tileset to its default when no style was applied.
* </p>
* <p>
* The style is applied to a tile before the {@link Cesium3DTileset#tileVisible}
* event is raised, so code in <code>tileVisible</code> can manually set a feature's
* properties (e.g. color and show) after the style is applied. When
* a new style is assigned any manually set properties are overwritten.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Cesium3DTileStyle}
*
* @default undefined
*
* @example
* tileset.style = new Cesium.Cesium3DTileStyle({
* color : {
* conditions : [
* ['${Height} >= 100', 'color("purple", 0.5)'],
* ['${Height} >= 50', 'color("red")'],
* ['true', 'color("blue")']
* ]
* },
* show : '${Height} > 0',
* meta : {
* description : '"Building id ${id} has height ${Height}."'
* }
* });
*
* @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}
*/
style : {
get : function() {
return this._styleEngine.style;
},
set : function(value) {
this._styleEngine.style = value;
}
},
/**
* The maximum screen space error used to drive level of detail refinement. This value helps determine when a tile
* refines to its descendants, and therefore plays a major role in balancing performance with visual quality.
* <p>
* A tile's screen space error is roughly equivalent to the number of pixels wide that would be drawn if a sphere with a
* radius equal to the tile's <b>geometric error</b> were rendered at the tile's position. If this value exceeds
* <code>maximumScreenSpaceError</code> the tile refines to its descendants.
* </p>
* <p>
* Depending on the tileset, <code>maximumScreenSpaceError</code> may need to be tweaked to achieve the right balance.
* Higher values provide better performance but lower visual quality.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Number}
* @default 16
*
* @exception {DeveloperError} <code>maximumScreenSpaceError</code> must be greater than or equal to zero.
*/
maximumScreenSpaceError : {
get : function() {
return this._maximumScreenSpaceError;
},
set : function(value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals('maximumScreenSpaceError', value, 0);
//>>includeEnd('debug');
this._maximumScreenSpaceError = value;
}
},
/**
* The maximum amount of GPU memory (in MB) that may be used to cache tiles. This value is estimated from
* geometry, textures, and batch table textures of loaded tiles. For point clouds, this value also
* includes per-point metadata.
* <p>
* Tiles not in view are unloaded to enforce this.
* </p>
* <p>
* If decreasing this value results in unloading tiles, the tiles are unloaded the next frame.
* </p>
* <p>
* If tiles sized more than <code>maximumMemoryUsage</code> are needed
* to meet the desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError},
* for the current view, then the memory usage of the tiles loaded will exceed
* <code>maximumMemoryUsage</code>. For example, if the maximum is 256 MB, but
* 300 MB of tiles are needed to meet the screen space error, then 300 MB of tiles may be loaded. When
* these tiles go out of view, they will be unloaded.
* </p>
*
* @memberof Cesium3DTileset.prototype
*
* @type {Number}
* @default 512
*
* @exception {DeveloperError} <code>maximumMemoryUsage</code> must be greater than or equal to zero.
* @see Cesium3DTileset#totalMemoryUsageInBytes
*/
maximumMemoryUsage : {
get : function() {
return this._maximumMemoryUsage;
},
set : function(value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals('value', value, 0);
//>>includeEnd('debug');
this._maximumMemoryUsage = value;
}
},
/**
* The tileset's bounding sphere.
*
* @memberof Cesium3DTileset.prototype
*
* @type {BoundingSphere}
* @readonly
*
* @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.
*
* @example
* var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
* url : 'http://localhost:8002/tilesets/Seattle'
* }));
*
* tileset.readyPromise.then(function(tileset) {
* // Set the camera to view the newly added tileset
* viewer.camera.viewBoundingSphere(tileset.boundingSphere, new Cesium.HeadingPitchRange(0, -0.5, 0));
* });
*/
boundingSphere : {
get : function() {
//>>includeStart('debug', pragmas.debug);
if (!this.ready) {
throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.');
}
//>>includeEnd('debug');
return this._root.boundingSphere;
}
},
/**
* A 4x4 transformation matrix that transforms the entire tileset.
*
* @memberof Cesium3DTileset.prototype
*
* @type {Matrix4}
* @default Matrix4.IDENTITY
*
* @example
* // Adjust a tileset's height from the globe's surface.
* var heightOffset = 20.0;
* var boundingSphere = tileset.boundingSphere;
* var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
* var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
* var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
* var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
* tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
*/
modelMatrix : {
get : function() {
return this._modelMatrix;
},
set : function(value) {
this._modelMatrix = Matrix4.clone(value, this._modelMatrix);
if (defined(this._root)) {
// Update the root transform right away instead of waiting for the next update loop.
// Useful, for example, when setting the modelMatrix and then having the camera view the tileset.
this._root.updateTransform(this._modelMatrix);
}
}
},
/**
* Returns the time, in milliseconds, since the tileset was loaded and first updated.
*
* @memberof Cesium3DTileset.prototype
*
* @type {Number}
* @readonly
*