UNPKG

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
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 *