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