123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995 |
- import BoundingSphere from "../Core/BoundingSphere.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Cartesian4 from "../Core/Cartesian4.js";
- import Cartographic from "../Core/Cartographic.js";
- import defined from "../Core/defined.js";
- import IndexDatatype from "../Core/IndexDatatype.js";
- import IntersectionTests from "../Core/IntersectionTests.js";
- import PixelFormat from "../Core/PixelFormat.js";
- import Ray from "../Core/Ray.js";
- import Request from "../Core/Request.js";
- import RequestState from "../Core/RequestState.js";
- import RequestType from "../Core/RequestType.js";
- import TerrainEncoding from "../Core/TerrainEncoding.js";
- import TileProviderError from "../Core/TileProviderError.js";
- import Buffer from "../Renderer/Buffer.js";
- import BufferUsage from "../Renderer/BufferUsage.js";
- import PixelDatatype from "../Renderer/PixelDatatype.js";
- import Sampler from "../Renderer/Sampler.js";
- import Texture from "../Renderer/Texture.js";
- import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
- import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
- import TextureWrap from "../Renderer/TextureWrap.js";
- import VertexArray from "../Renderer/VertexArray.js";
- import ImageryState from "./ImageryState.js";
- import QuadtreeTileLoadState from "./QuadtreeTileLoadState.js";
- import SceneMode from "./SceneMode.js";
- import TerrainState from "./TerrainState.js";
- /**
- * Contains additional information about a {@link QuadtreeTile} of the globe's surface, and
- * encapsulates state transition logic for loading tiles.
- *
- * @constructor
- * @alias GlobeSurfaceTile
- * @private
- */
- function GlobeSurfaceTile() {
- /**
- * The {@link TileImagery} attached to this tile.
- * @type {TileImagery[]}
- * @default []
- */
- this.imagery = [];
- this.waterMaskTexture = undefined;
- this.waterMaskTranslationAndScale = new Cartesian4(0.0, 0.0, 1.0, 1.0);
- this.terrainData = undefined;
- this.vertexArray = undefined;
- /**
- * A bounding region used to estimate distance to the tile. The horizontal bounds are always tight-fitting,
- * but the `minimumHeight` and `maximumHeight` properties may be derived from the min/max of an ancestor tile
- * and be quite loose-fitting and thus very poor for estimating distance.
- * @type {TileBoundingRegion}
- */
- this.tileBoundingRegion = undefined;
- this.occludeePointInScaledSpace = new Cartesian3();
- this.boundingVolumeSourceTile = undefined;
- this.boundingVolumeIsFromMesh = false;
- this.terrainState = TerrainState.UNLOADED;
- this.mesh = undefined;
- this.fill = undefined;
- this.pickBoundingSphere = new BoundingSphere();
- this.surfaceShader = undefined;
- this.isClipped = true;
- this.clippedByBoundaries = false;
- }
- Object.defineProperties(GlobeSurfaceTile.prototype, {
- /**
- * Gets a value indicating whether or not this tile is eligible to be unloaded.
- * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
- * such as a request for data, is in progress on it. A tile will never be
- * unloaded while it is needed for rendering, regardless of the value of this
- * property.
- * @memberof GlobeSurfaceTile.prototype
- * @type {Boolean}
- */
- eligibleForUnloading: {
- get: function () {
- // Do not remove tiles that are transitioning or that have
- // imagery that is transitioning.
- const terrainState = this.terrainState;
- const loadingIsTransitioning =
- terrainState === TerrainState.RECEIVING ||
- terrainState === TerrainState.TRANSFORMING;
- let shouldRemoveTile = !loadingIsTransitioning;
- const imagery = this.imagery;
- for (let i = 0, len = imagery.length; shouldRemoveTile && i < len; ++i) {
- const tileImagery = imagery[i];
- shouldRemoveTile =
- !defined(tileImagery.loadingImagery) ||
- tileImagery.loadingImagery.state !== ImageryState.TRANSITIONING;
- }
- return shouldRemoveTile;
- },
- },
- /**
- * Gets the {@link TerrainMesh} that is used for rendering this tile, if any.
- * Returns the value of the {@link GlobeSurfaceTile#mesh} property if
- * {@link GlobeSurfaceTile#vertexArray} is defined. Otherwise, It returns the
- * {@link TerrainFillMesh#mesh} property of the {@link GlobeSurfaceTile#fill}.
- * If there is no fill, it returns undefined.
- *
- * @memberof GlobeSurfaceTile.prototype
- * @type {TerrainMesh}
- */
- renderedMesh: {
- get: function () {
- if (defined(this.vertexArray)) {
- return this.mesh;
- } else if (defined(this.fill)) {
- return this.fill.mesh;
- }
- return undefined;
- },
- },
- });
- const scratchCartographic = new Cartographic();
- function getPosition(encoding, mode, projection, vertices, index, result) {
- let position = encoding.getExaggeratedPosition(vertices, index, result);
- if (defined(mode) && mode !== SceneMode.SCENE3D) {
- const ellipsoid = projection.ellipsoid;
- const positionCartographic = ellipsoid.cartesianToCartographic(
- position,
- scratchCartographic
- );
- position = projection.project(positionCartographic, result);
- position = Cartesian3.fromElements(
- position.z,
- position.x,
- position.y,
- result
- );
- }
- return position;
- }
- const scratchV0 = new Cartesian3();
- const scratchV1 = new Cartesian3();
- const scratchV2 = new Cartesian3();
- GlobeSurfaceTile.prototype.pick = function (
- ray,
- mode,
- projection,
- cullBackFaces,
- result
- ) {
- const mesh = this.renderedMesh;
- if (!defined(mesh)) {
- return undefined;
- }
- const vertices = mesh.vertices;
- const indices = mesh.indices;
- const encoding = mesh.encoding;
- const indicesLength = indices.length;
- let minT = Number.MAX_VALUE;
- for (let i = 0; i < indicesLength; i += 3) {
- const i0 = indices[i];
- const i1 = indices[i + 1];
- const i2 = indices[i + 2];
- const v0 = getPosition(encoding, mode, projection, vertices, i0, scratchV0);
- const v1 = getPosition(encoding, mode, projection, vertices, i1, scratchV1);
- const v2 = getPosition(encoding, mode, projection, vertices, i2, scratchV2);
- const t = IntersectionTests.rayTriangleParametric(
- ray,
- v0,
- v1,
- v2,
- cullBackFaces
- );
- if (defined(t) && t < minT && t >= 0.0) {
- minT = t;
- }
- }
- return minT !== Number.MAX_VALUE
- ? Ray.getPoint(ray, minT, result)
- : undefined;
- };
- GlobeSurfaceTile.prototype.freeResources = function () {
- if (defined(this.waterMaskTexture)) {
- --this.waterMaskTexture.referenceCount;
- if (this.waterMaskTexture.referenceCount === 0) {
- this.waterMaskTexture.destroy();
- }
- this.waterMaskTexture = undefined;
- }
- this.terrainData = undefined;
- this.terrainState = TerrainState.UNLOADED;
- this.mesh = undefined;
- this.fill = this.fill && this.fill.destroy();
- const imageryList = this.imagery;
- for (let i = 0, len = imageryList.length; i < len; ++i) {
- imageryList[i].freeResources();
- }
- this.imagery.length = 0;
- this.freeVertexArray();
- };
- GlobeSurfaceTile.prototype.freeVertexArray = function () {
- GlobeSurfaceTile._freeVertexArray(this.vertexArray);
- this.vertexArray = undefined;
- GlobeSurfaceTile._freeVertexArray(this.wireframeVertexArray);
- this.wireframeVertexArray = undefined;
- };
- GlobeSurfaceTile.initialize = function (
- tile,
- terrainProvider,
- imageryLayerCollection
- ) {
- let surfaceTile = tile.data;
- if (!defined(surfaceTile)) {
- surfaceTile = tile.data = new GlobeSurfaceTile();
- }
- if (tile.state === QuadtreeTileLoadState.START) {
- prepareNewTile(tile, terrainProvider, imageryLayerCollection);
- tile.state = QuadtreeTileLoadState.LOADING;
- }
- };
- GlobeSurfaceTile.processStateMachine = function (
- tile,
- frameState,
- terrainProvider,
- imageryLayerCollection,
- quadtree,
- vertexArraysToDestroy,
- terrainOnly
- ) {
- GlobeSurfaceTile.initialize(tile, terrainProvider, imageryLayerCollection);
- const surfaceTile = tile.data;
- if (tile.state === QuadtreeTileLoadState.LOADING) {
- processTerrainStateMachine(
- tile,
- frameState,
- terrainProvider,
- imageryLayerCollection,
- quadtree,
- vertexArraysToDestroy
- );
- }
- // From here down we're loading imagery, not terrain. We don't want to load imagery until
- // we're certain that the terrain tiles are actually visible, though. We'll load terrainOnly
- // in these scenarios:
- // * our bounding volume isn't accurate so we're not certain this tile is really visible (see GlobeSurfaceTileProvider#loadTile).
- // * we want to upsample from this tile but don't plan to render it (see processTerrainStateMachine).
- if (terrainOnly) {
- return;
- }
- const wasAlreadyRenderable = tile.renderable;
- // The terrain is renderable as soon as we have a valid vertex array.
- tile.renderable = defined(surfaceTile.vertexArray);
- // But it's not done loading until it's in the READY state.
- const isTerrainDoneLoading = surfaceTile.terrainState === TerrainState.READY;
- // If this tile's terrain and imagery are just upsampled from its parent, mark the tile as
- // upsampled only. We won't refine a tile if its four children are upsampled only.
- tile.upsampledFromParent =
- defined(surfaceTile.terrainData) &&
- surfaceTile.terrainData.wasCreatedByUpsampling();
- const isImageryDoneLoading = surfaceTile.processImagery(
- tile,
- terrainProvider,
- frameState
- );
- if (isTerrainDoneLoading && isImageryDoneLoading) {
- const callbacks = tile._loadedCallbacks;
- const newCallbacks = {};
- for (const layerId in callbacks) {
- if (callbacks.hasOwnProperty(layerId)) {
- if (!callbacks[layerId](tile)) {
- newCallbacks[layerId] = callbacks[layerId];
- }
- }
- }
- tile._loadedCallbacks = newCallbacks;
- tile.state = QuadtreeTileLoadState.DONE;
- }
- // Once a tile is renderable, it stays renderable, because doing otherwise would
- // cause detail (or maybe even the entire globe) to vanish when adding a new
- // imagery layer. `GlobeSurfaceTileProvider._onLayerAdded` sets renderable to
- // false for all affected tiles that are not currently being rendered.
- if (wasAlreadyRenderable) {
- tile.renderable = true;
- }
- };
- GlobeSurfaceTile.prototype.processImagery = function (
- tile,
- terrainProvider,
- frameState,
- skipLoading
- ) {
- const surfaceTile = tile.data;
- let isUpsampledOnly = tile.upsampledFromParent;
- let isAnyTileLoaded = false;
- let isDoneLoading = true;
- // Transition imagery states
- const tileImageryCollection = surfaceTile.imagery;
- let i, len;
- for (i = 0, len = tileImageryCollection.length; i < len; ++i) {
- const tileImagery = tileImageryCollection[i];
- if (!defined(tileImagery.loadingImagery)) {
- isUpsampledOnly = false;
- continue;
- }
- if (tileImagery.loadingImagery.state === ImageryState.PLACEHOLDER) {
- const imageryLayer = tileImagery.loadingImagery.imageryLayer;
- if (imageryLayer.imageryProvider.ready) {
- // Remove the placeholder and add the actual skeletons (if any)
- // at the same position. Then continue the loop at the same index.
- tileImagery.freeResources();
- tileImageryCollection.splice(i, 1);
- imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i);
- --i;
- len = tileImageryCollection.length;
- continue;
- } else {
- isUpsampledOnly = false;
- }
- }
- const thisTileDoneLoading = tileImagery.processStateMachine(
- tile,
- frameState,
- skipLoading
- );
- isDoneLoading = isDoneLoading && thisTileDoneLoading;
- // The imagery is renderable as soon as we have any renderable imagery for this region.
- isAnyTileLoaded =
- isAnyTileLoaded ||
- thisTileDoneLoading ||
- defined(tileImagery.readyImagery);
- isUpsampledOnly =
- isUpsampledOnly &&
- defined(tileImagery.loadingImagery) &&
- (tileImagery.loadingImagery.state === ImageryState.FAILED ||
- tileImagery.loadingImagery.state === ImageryState.INVALID);
- }
- tile.upsampledFromParent = isUpsampledOnly;
- // Allow rendering if any available layers are loaded
- tile.renderable = tile.renderable && (isAnyTileLoaded || isDoneLoading);
- return isDoneLoading;
- };
- function toggleGeodeticSurfaceNormals(
- surfaceTile,
- enabled,
- ellipsoid,
- frameState
- ) {
- const renderedMesh = surfaceTile.renderedMesh;
- const vertexBuffer = renderedMesh.vertices;
- const encoding = renderedMesh.encoding;
- const vertexCount = vertexBuffer.length / encoding.stride;
- // Calculate the new stride and generate a new buffer
- // Clone the other encoding, toggle geodetic surface normals, then clone again to get updated stride
- let newEncoding = TerrainEncoding.clone(encoding);
- newEncoding.hasGeodeticSurfaceNormals = enabled;
- newEncoding = TerrainEncoding.clone(newEncoding);
- const newStride = newEncoding.stride;
- const newVertexBuffer = new Float32Array(vertexCount * newStride);
- if (enabled) {
- encoding.addGeodeticSurfaceNormals(
- vertexBuffer,
- newVertexBuffer,
- ellipsoid
- );
- } else {
- encoding.removeGeodeticSurfaceNormals(vertexBuffer, newVertexBuffer);
- }
- renderedMesh.vertices = newVertexBuffer;
- renderedMesh.stride = newStride;
- // delete the old vertex array (which deletes the vertex buffer attached to it), and create a new vertex array with the new vertex buffer
- const isFill = renderedMesh !== surfaceTile.mesh;
- if (isFill) {
- GlobeSurfaceTile._freeVertexArray(surfaceTile.fill.vertexArray);
- surfaceTile.fill.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
- frameState.context,
- renderedMesh
- );
- } else {
- GlobeSurfaceTile._freeVertexArray(surfaceTile.vertexArray);
- surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
- frameState.context,
- renderedMesh
- );
- }
- GlobeSurfaceTile._freeVertexArray(surfaceTile.wireframeVertexArray);
- surfaceTile.wireframeVertexArray = undefined;
- }
- GlobeSurfaceTile.prototype.addGeodeticSurfaceNormals = function (
- ellipsoid,
- frameState
- ) {
- toggleGeodeticSurfaceNormals(this, true, ellipsoid, frameState);
- };
- GlobeSurfaceTile.prototype.removeGeodeticSurfaceNormals = function (
- frameState
- ) {
- toggleGeodeticSurfaceNormals(this, false, undefined, frameState);
- };
- GlobeSurfaceTile.prototype.updateExaggeration = function (
- tile,
- frameState,
- quadtree
- ) {
- const surfaceTile = this;
- const mesh = surfaceTile.renderedMesh;
- if (mesh === undefined) {
- return;
- }
- // Check the tile's terrain encoding to see if it has been exaggerated yet
- const exaggeration = frameState.terrainExaggeration;
- const exaggerationRelativeHeight =
- frameState.terrainExaggerationRelativeHeight;
- const hasExaggerationScale = exaggeration !== 1.0;
- const encoding = mesh.encoding;
- const encodingExaggerationScaleChanged =
- encoding.exaggeration !== exaggeration;
- const encodingRelativeHeightChanged =
- encoding.exaggerationRelativeHeight !== exaggerationRelativeHeight;
- if (encodingExaggerationScaleChanged || encodingRelativeHeightChanged) {
- // Turning exaggeration scale on/off requires adding or removing geodetic surface normals
- // Relative height only translates, so it has no effect on normals
- if (encodingExaggerationScaleChanged) {
- if (hasExaggerationScale && !encoding.hasGeodeticSurfaceNormals) {
- const ellipsoid = tile.tilingScheme.ellipsoid;
- surfaceTile.addGeodeticSurfaceNormals(ellipsoid, frameState);
- } else if (!hasExaggerationScale && encoding.hasGeodeticSurfaceNormals) {
- surfaceTile.removeGeodeticSurfaceNormals(frameState);
- }
- }
- encoding.exaggeration = exaggeration;
- encoding.exaggerationRelativeHeight = exaggerationRelativeHeight;
- // Notify the quadtree that this tile's height has changed
- if (quadtree !== undefined) {
- quadtree._tileToUpdateHeights.push(tile);
- const customData = tile.customData;
- const customDataLength = customData.length;
- for (let i = 0; i < customDataLength; i++) {
- // Restart the level so that a height update is triggered
- const data = customData[i];
- data.level = -1;
- }
- }
- }
- };
- function prepareNewTile(tile, terrainProvider, imageryLayerCollection) {
- let available = terrainProvider.getTileDataAvailable(
- tile.x,
- tile.y,
- tile.level
- );
- if (!defined(available) && defined(tile.parent)) {
- // Provider doesn't know if this tile is available. Does the parent tile know?
- const parent = tile.parent;
- const parentSurfaceTile = parent.data;
- if (defined(parentSurfaceTile) && defined(parentSurfaceTile.terrainData)) {
- available = parentSurfaceTile.terrainData.isChildAvailable(
- parent.x,
- parent.y,
- tile.x,
- tile.y
- );
- }
- }
- if (available === false) {
- // This tile is not available, so mark it failed so we start upsampling right away.
- tile.data.terrainState = TerrainState.FAILED;
- }
- // Map imagery tiles to this terrain tile
- for (let i = 0, len = imageryLayerCollection.length; i < len; ++i) {
- const layer = imageryLayerCollection.get(i);
- if (layer.show) {
- layer._createTileImagerySkeletons(tile, terrainProvider);
- }
- }
- }
- function processTerrainStateMachine(
- tile,
- frameState,
- terrainProvider,
- imageryLayerCollection,
- quadtree,
- vertexArraysToDestroy
- ) {
- const surfaceTile = tile.data;
- // If this tile is FAILED, we'll need to upsample from the parent. If the parent isn't
- // ready for that, let's push it along.
- const parent = tile.parent;
- if (
- surfaceTile.terrainState === TerrainState.FAILED &&
- parent !== undefined
- ) {
- const parentReady =
- parent.data !== undefined &&
- parent.data.terrainData !== undefined &&
- parent.data.terrainData.canUpsample !== false;
- if (!parentReady) {
- GlobeSurfaceTile.processStateMachine(
- parent,
- frameState,
- terrainProvider,
- imageryLayerCollection,
- quadtree,
- vertexArraysToDestroy,
- true
- );
- }
- }
- if (surfaceTile.terrainState === TerrainState.FAILED) {
- upsample(
- surfaceTile,
- tile,
- frameState,
- terrainProvider,
- tile.x,
- tile.y,
- tile.level
- );
- }
- if (surfaceTile.terrainState === TerrainState.UNLOADED) {
- requestTileGeometry(
- surfaceTile,
- terrainProvider,
- tile.x,
- tile.y,
- tile.level
- );
- }
- if (surfaceTile.terrainState === TerrainState.RECEIVED) {
- transform(
- surfaceTile,
- frameState,
- terrainProvider,
- tile.x,
- tile.y,
- tile.level
- );
- }
- if (surfaceTile.terrainState === TerrainState.TRANSFORMED) {
- createResources(
- surfaceTile,
- frameState.context,
- terrainProvider,
- tile.x,
- tile.y,
- tile.level,
- vertexArraysToDestroy
- );
- // Update the tile's exaggeration in case the globe's exaggeration changed while the tile was being processed
- surfaceTile.updateExaggeration(tile, frameState, quadtree);
- }
- if (
- surfaceTile.terrainState >= TerrainState.RECEIVED &&
- surfaceTile.waterMaskTexture === undefined &&
- terrainProvider.hasWaterMask
- ) {
- const terrainData = surfaceTile.terrainData;
- if (terrainData.waterMask !== undefined) {
- createWaterMaskTextureIfNeeded(frameState.context, surfaceTile);
- } else {
- const sourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
- if (defined(sourceTile) && defined(sourceTile.data.waterMaskTexture)) {
- surfaceTile.waterMaskTexture = sourceTile.data.waterMaskTexture;
- ++surfaceTile.waterMaskTexture.referenceCount;
- surfaceTile._computeWaterMaskTranslationAndScale(
- tile,
- sourceTile,
- surfaceTile.waterMaskTranslationAndScale
- );
- }
- }
- }
- }
- function upsample(surfaceTile, tile, frameState, terrainProvider, x, y, level) {
- const parent = tile.parent;
- if (!parent) {
- // Trying to upsample from a root tile. No can do. This tile is a failure.
- tile.state = QuadtreeTileLoadState.FAILED;
- return;
- }
- const sourceData = parent.data.terrainData;
- const sourceX = parent.x;
- const sourceY = parent.y;
- const sourceLevel = parent.level;
- if (!defined(sourceData)) {
- // Parent is not available, so we can't upsample this tile yet.
- return;
- }
- const terrainDataPromise = sourceData.upsample(
- terrainProvider.tilingScheme,
- sourceX,
- sourceY,
- sourceLevel,
- x,
- y,
- level
- );
- if (!defined(terrainDataPromise)) {
- // The upsample request has been deferred - try again later.
- return;
- }
- surfaceTile.terrainState = TerrainState.RECEIVING;
- Promise.resolve(terrainDataPromise)
- .then(function (terrainData) {
- surfaceTile.terrainData = terrainData;
- surfaceTile.terrainState = TerrainState.RECEIVED;
- })
- .catch(function () {
- surfaceTile.terrainState = TerrainState.FAILED;
- });
- }
- function requestTileGeometry(surfaceTile, terrainProvider, x, y, level) {
- function success(terrainData) {
- surfaceTile.terrainData = terrainData;
- surfaceTile.terrainState = TerrainState.RECEIVED;
- surfaceTile.request = undefined;
- }
- function failure(error) {
- if (surfaceTile.request.state === RequestState.CANCELLED) {
- // Cancelled due to low priority - try again later.
- surfaceTile.terrainData = undefined;
- surfaceTile.terrainState = TerrainState.UNLOADED;
- surfaceTile.request = undefined;
- return;
- }
- // Initially assume failure. handleError may retry, in which case the state will
- // change to RECEIVING or UNLOADED.
- surfaceTile.terrainState = TerrainState.FAILED;
- surfaceTile.request = undefined;
- const message = `Failed to obtain terrain tile X: ${x} Y: ${y} Level: ${level}. Error message: "${error}"`;
- terrainProvider._requestError = TileProviderError.handleError(
- terrainProvider._requestError,
- terrainProvider,
- terrainProvider.errorEvent,
- message,
- x,
- y,
- level,
- doRequest
- );
- }
- function doRequest() {
- // Request the terrain from the terrain provider.
- const request = new Request({
- throttle: false,
- throttleByServer: true,
- type: RequestType.TERRAIN,
- });
- surfaceTile.request = request;
- const requestPromise = terrainProvider.requestTileGeometry(
- x,
- y,
- level,
- request
- );
- // If the request method returns undefined (instead of a promise), the request
- // has been deferred.
- if (defined(requestPromise)) {
- surfaceTile.terrainState = TerrainState.RECEIVING;
- Promise.resolve(requestPromise)
- .then(function (terrainData) {
- success(terrainData);
- })
- .catch(function (e) {
- failure(e);
- });
- } else {
- // Deferred - try again later.
- surfaceTile.terrainState = TerrainState.UNLOADED;
- surfaceTile.request = undefined;
- }
- }
- doRequest();
- }
- const scratchCreateMeshOptions = {
- tilingScheme: undefined,
- x: 0,
- y: 0,
- level: 0,
- exaggeration: 1.0,
- exaggerationRelativeHeight: 0.0,
- throttle: true,
- };
- function transform(surfaceTile, frameState, terrainProvider, x, y, level) {
- const tilingScheme = terrainProvider.tilingScheme;
- const createMeshOptions = scratchCreateMeshOptions;
- createMeshOptions.tilingScheme = tilingScheme;
- createMeshOptions.x = x;
- createMeshOptions.y = y;
- createMeshOptions.level = level;
- createMeshOptions.exaggeration = frameState.terrainExaggeration;
- createMeshOptions.exaggerationRelativeHeight =
- frameState.terrainExaggerationRelativeHeight;
- createMeshOptions.throttle = true;
- const terrainData = surfaceTile.terrainData;
- const meshPromise = terrainData.createMesh(createMeshOptions);
- if (!defined(meshPromise)) {
- // Postponed.
- return;
- }
- surfaceTile.terrainState = TerrainState.TRANSFORMING;
- Promise.resolve(meshPromise)
- .then(function (mesh) {
- surfaceTile.mesh = mesh;
- surfaceTile.terrainState = TerrainState.TRANSFORMED;
- })
- .catch(function () {
- surfaceTile.terrainState = TerrainState.FAILED;
- });
- }
- GlobeSurfaceTile._createVertexArrayForMesh = function (context, mesh) {
- const typedArray = mesh.vertices;
- const buffer = Buffer.createVertexBuffer({
- context: context,
- typedArray: typedArray,
- usage: BufferUsage.STATIC_DRAW,
- });
- const attributes = mesh.encoding.getAttributes(buffer);
- const indexBuffers = mesh.indices.indexBuffers || {};
- let indexBuffer = indexBuffers[context.id];
- if (!defined(indexBuffer) || indexBuffer.isDestroyed()) {
- const indices = mesh.indices;
- indexBuffer = Buffer.createIndexBuffer({
- context: context,
- typedArray: indices,
- usage: BufferUsage.STATIC_DRAW,
- indexDatatype: IndexDatatype.fromSizeInBytes(indices.BYTES_PER_ELEMENT),
- });
- indexBuffer.vertexArrayDestroyable = false;
- indexBuffer.referenceCount = 1;
- indexBuffers[context.id] = indexBuffer;
- mesh.indices.indexBuffers = indexBuffers;
- } else {
- ++indexBuffer.referenceCount;
- }
- return new VertexArray({
- context: context,
- attributes: attributes,
- indexBuffer: indexBuffer,
- });
- };
- GlobeSurfaceTile._freeVertexArray = function (vertexArray) {
- if (defined(vertexArray)) {
- const indexBuffer = vertexArray.indexBuffer;
- if (!vertexArray.isDestroyed()) {
- vertexArray.destroy();
- }
- if (
- defined(indexBuffer) &&
- !indexBuffer.isDestroyed() &&
- defined(indexBuffer.referenceCount)
- ) {
- --indexBuffer.referenceCount;
- if (indexBuffer.referenceCount === 0) {
- indexBuffer.destroy();
- }
- }
- }
- };
- function createResources(
- surfaceTile,
- context,
- terrainProvider,
- x,
- y,
- level,
- vertexArraysToDestroy
- ) {
- surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
- context,
- surfaceTile.mesh
- );
- surfaceTile.terrainState = TerrainState.READY;
- surfaceTile.fill =
- surfaceTile.fill && surfaceTile.fill.destroy(vertexArraysToDestroy);
- }
- function getContextWaterMaskData(context) {
- let data = context.cache.tile_waterMaskData;
- if (!defined(data)) {
- const allWaterTexture = Texture.create({
- context: context,
- pixelFormat: PixelFormat.LUMINANCE,
- pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
- source: {
- arrayBufferView: new Uint8Array([255]),
- width: 1,
- height: 1,
- },
- });
- allWaterTexture.referenceCount = 1;
- const sampler = new Sampler({
- wrapS: TextureWrap.CLAMP_TO_EDGE,
- wrapT: TextureWrap.CLAMP_TO_EDGE,
- minificationFilter: TextureMinificationFilter.LINEAR,
- magnificationFilter: TextureMagnificationFilter.LINEAR,
- });
- data = {
- allWaterTexture: allWaterTexture,
- sampler: sampler,
- destroy: function () {
- this.allWaterTexture.destroy();
- },
- };
- context.cache.tile_waterMaskData = data;
- }
- return data;
- }
- function createWaterMaskTextureIfNeeded(context, surfaceTile) {
- const waterMask = surfaceTile.terrainData.waterMask;
- const waterMaskData = getContextWaterMaskData(context);
- let texture;
- const waterMaskLength = waterMask.length;
- if (waterMaskLength === 1) {
- // Length 1 means the tile is entirely land or entirely water.
- // A value of 0 indicates entirely land, a value of 1 indicates entirely water.
- if (waterMask[0] !== 0) {
- texture = waterMaskData.allWaterTexture;
- } else {
- // Leave the texture undefined if the tile is entirely land.
- return;
- }
- } else {
- const textureSize = Math.sqrt(waterMaskLength);
- texture = Texture.create({
- context: context,
- pixelFormat: PixelFormat.LUMINANCE,
- pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
- source: {
- width: textureSize,
- height: textureSize,
- arrayBufferView: waterMask,
- },
- sampler: waterMaskData.sampler,
- flipY: false,
- });
- texture.referenceCount = 0;
- }
- ++texture.referenceCount;
- surfaceTile.waterMaskTexture = texture;
- Cartesian4.fromElements(
- 0.0,
- 0.0,
- 1.0,
- 1.0,
- surfaceTile.waterMaskTranslationAndScale
- );
- }
- GlobeSurfaceTile.prototype._findAncestorTileWithTerrainData = function (tile) {
- let sourceTile = tile.parent;
- while (
- defined(sourceTile) &&
- (!defined(sourceTile.data) ||
- !defined(sourceTile.data.terrainData) ||
- sourceTile.data.terrainData.wasCreatedByUpsampling())
- ) {
- sourceTile = sourceTile.parent;
- }
- return sourceTile;
- };
- GlobeSurfaceTile.prototype._computeWaterMaskTranslationAndScale = function (
- tile,
- sourceTile,
- result
- ) {
- const sourceTileRectangle = sourceTile.rectangle;
- const tileRectangle = tile.rectangle;
- const tileWidth = tileRectangle.width;
- const tileHeight = tileRectangle.height;
- const scaleX = tileWidth / sourceTileRectangle.width;
- const scaleY = tileHeight / sourceTileRectangle.height;
- result.x =
- (scaleX * (tileRectangle.west - sourceTileRectangle.west)) / tileWidth;
- result.y =
- (scaleY * (tileRectangle.south - sourceTileRectangle.south)) / tileHeight;
- result.z = scaleX;
- result.w = scaleY;
- return result;
- };
- export default GlobeSurfaceTile;
|