| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818 | import BoundingSphere from "../Core/BoundingSphere.js";import BoxOutlineGeometry from "../Core/BoxOutlineGeometry.js";import Cartesian2 from "../Core/Cartesian2.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartesian4 from "../Core/Cartesian4.js";import Cartographic from "../Core/Cartographic.js";import clone from "../Core/clone.js";import Color from "../Core/Color.js";import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";import combine from "../Core/combine.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import Event from "../Core/Event.js";import GeometryInstance from "../Core/GeometryInstance.js";import GeometryPipeline from "../Core/GeometryPipeline.js";import IndexDatatype from "../Core/IndexDatatype.js";import Intersect from "../Core/Intersect.js";import CesiumMath from "../Core/Math.js";import Matrix4 from "../Core/Matrix4.js";import NearFarScalar from "../Core/NearFarScalar.js";import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";import OrthographicFrustum from "../Core/OrthographicFrustum.js";import PrimitiveType from "../Core/PrimitiveType.js";import Rectangle from "../Core/Rectangle.js";import SphereOutlineGeometry from "../Core/SphereOutlineGeometry.js";import TerrainExaggeration from "../Core/TerrainExaggeration.js";import TerrainQuantization from "../Core/TerrainQuantization.js";import Visibility from "../Core/Visibility.js";import WebMercatorProjection from "../Core/WebMercatorProjection.js";import Buffer from "../Renderer/Buffer.js";import BufferUsage from "../Renderer/BufferUsage.js";import ContextLimits from "../Renderer/ContextLimits.js";import DrawCommand from "../Renderer/DrawCommand.js";import Pass from "../Renderer/Pass.js";import RenderState from "../Renderer/RenderState.js";import VertexArray from "../Renderer/VertexArray.js";import BlendingState from "./BlendingState.js";import ClippingPlaneCollection from "./ClippingPlaneCollection.js";import DepthFunction from "./DepthFunction.js";import GlobeSurfaceTile from "./GlobeSurfaceTile.js";import ImageryLayer from "./ImageryLayer.js";import ImageryState from "./ImageryState.js";import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";import Primitive from "./Primitive.js";import QuadtreeTileLoadState from "./QuadtreeTileLoadState.js";import SceneMode from "./SceneMode.js";import ShadowMode from "./ShadowMode.js";import TerrainFillMesh from "./TerrainFillMesh.js";import TerrainState from "./TerrainState.js";import TileBoundingRegion from "./TileBoundingRegion.js";import TileSelectionResult from "./TileSelectionResult.js";/** * Provides quadtree tiles representing the surface of the globe.  This type is intended to be used * with {@link QuadtreePrimitive}. * * @alias GlobeSurfaceTileProvider * @constructor * * @param {TerrainProvider} options.terrainProvider The terrain provider that describes the surface geometry. * @param {ImageryLayerCollection} option.imageryLayers The collection of imagery layers describing the shading of the surface. * @param {GlobeSurfaceShaderSet} options.surfaceShaderSet The set of shaders used to render the surface. * * @private */function GlobeSurfaceTileProvider(options) {  //>>includeStart('debug', pragmas.debug);  if (!defined(options)) {    throw new DeveloperError("options is required.");  }  if (!defined(options.terrainProvider)) {    throw new DeveloperError("options.terrainProvider is required.");  } else if (!defined(options.imageryLayers)) {    throw new DeveloperError("options.imageryLayers is required.");  } else if (!defined(options.surfaceShaderSet)) {    throw new DeveloperError("options.surfaceShaderSet is required.");  }  //>>includeEnd('debug');  this.lightingFadeOutDistance = 6500000.0;  this.lightingFadeInDistance = 9000000.0;  this.hasWaterMask = false;  this.oceanNormalMap = undefined;  this.zoomedOutOceanSpecularIntensity = 0.5;  this.enableLighting = false;  this.dynamicAtmosphereLighting = false;  this.dynamicAtmosphereLightingFromSun = false;  this.showGroundAtmosphere = false;  this.shadows = ShadowMode.RECEIVE_ONLY;  /**   * The color to use to highlight terrain fill tiles. If undefined, fill tiles are not   * highlighted at all. The alpha value is used to alpha blend with the tile's   * actual color. Because terrain fill tiles do not represent the actual terrain surface,   * it may be useful in some applications to indicate visually that they are not to be trusted.   * @type {Color}   * @default undefined   */  this.fillHighlightColor = undefined;  this.hueShift = 0.0;  this.saturationShift = 0.0;  this.brightnessShift = 0.0;  this.showSkirts = true;  this.backFaceCulling = true;  this.undergroundColor = undefined;  this.undergroundColorAlphaByDistance = undefined;  this.lambertDiffuseMultiplier = 0.0;  this.materialUniformMap = undefined;  this._materialUniformMap = undefined;  this._quadtree = undefined;  this._terrainProvider = options.terrainProvider;  this._imageryLayers = options.imageryLayers;  this._surfaceShaderSet = options.surfaceShaderSet;  this._renderState = undefined;  this._blendRenderState = undefined;  this._disableCullingRenderState = undefined;  this._disableCullingBlendRenderState = undefined;  this._errorEvent = new Event();  this._imageryLayers.layerAdded.addEventListener(    GlobeSurfaceTileProvider.prototype._onLayerAdded,    this  );  this._imageryLayers.layerRemoved.addEventListener(    GlobeSurfaceTileProvider.prototype._onLayerRemoved,    this  );  this._imageryLayers.layerMoved.addEventListener(    GlobeSurfaceTileProvider.prototype._onLayerMoved,    this  );  this._imageryLayers.layerShownOrHidden.addEventListener(    GlobeSurfaceTileProvider.prototype._onLayerShownOrHidden,    this  );  this._imageryLayersUpdatedEvent = new Event();  this._layerOrderChanged = false;  this._tilesToRenderByTextureCount = [];  this._drawCommands = [];  this._uniformMaps = [];  this._usedDrawCommands = 0;  this._vertexArraysToDestroy = [];  this._debug = {    wireframe: false,    boundingSphereTile: undefined,  };  this._baseColor = undefined;  this._firstPassInitialColor = undefined;  this.baseColor = new Color(0.0, 0.0, 0.5, 1.0);  /**   * A property specifying a {@link ClippingPlaneCollection} used to selectively disable rendering on the outside of each plane.   * @type {ClippingPlaneCollection}   * @private   */  this._clippingPlanes = undefined;  /**   * A property specifying a {@link Rectangle} used to selectively limit terrain and imagery rendering.   * @type {Rectangle}   */  this.cartographicLimitRectangle = Rectangle.clone(Rectangle.MAX_VALUE);  this._hasLoadedTilesThisFrame = false;  this._hasFillTilesThisFrame = false;  this._oldTerrainExaggeration = undefined;  this._oldTerrainExaggerationRelativeHeight = undefined;}Object.defineProperties(GlobeSurfaceTileProvider.prototype, {  /**   * Gets or sets the color of the globe when no imagery is available.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {Color}   */  baseColor: {    get: function () {      return this._baseColor;    },    set: function (value) {      //>>includeStart('debug', pragmas.debug);      if (!defined(value)) {        throw new DeveloperError("value is required.");      }      //>>includeEnd('debug');      this._baseColor = value;      this._firstPassInitialColor = Cartesian4.fromColor(        value,        this._firstPassInitialColor      );    },  },  /**   * Gets or sets the {@link QuadtreePrimitive} for which this provider is   * providing tiles.  This property may be undefined if the provider is not yet associated   * with a {@link QuadtreePrimitive}.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {QuadtreePrimitive}   */  quadtree: {    get: function () {      return this._quadtree;    },    set: function (value) {      //>>includeStart('debug', pragmas.debug);      if (!defined(value)) {        throw new DeveloperError("value is required.");      }      //>>includeEnd('debug');      this._quadtree = value;    },  },  /**   * Gets a value indicating whether or not the provider is ready for use.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {Boolean}   */  ready: {    get: function () {      return (        this._terrainProvider.ready &&        (this._imageryLayers.length === 0 ||          this._imageryLayers.get(0).imageryProvider.ready)      );    },  },  /**   * Gets the tiling scheme used by the provider.  This property should   * not be accessed before {@link GlobeSurfaceTileProvider#ready} returns true.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {TilingScheme}   */  tilingScheme: {    get: function () {      return this._terrainProvider.tilingScheme;    },  },  /**   * Gets an event that is raised when the geometry provider encounters an asynchronous error.  By subscribing   * to the event, you will be notified of the error and can potentially recover from it.  Event listeners   * are passed an instance of {@link TileProviderError}.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {Event}   */  errorEvent: {    get: function () {      return this._errorEvent;    },  },  /**   * Gets an event that is raised when an imagery layer is added, shown, hidden, moved, or removed.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {Event}   */  imageryLayersUpdatedEvent: {    get: function () {      return this._imageryLayersUpdatedEvent;    },  },  /**   * Gets or sets the terrain provider that describes the surface geometry.   * @memberof GlobeSurfaceTileProvider.prototype   * @type {TerrainProvider}   */  terrainProvider: {    get: function () {      return this._terrainProvider;    },    set: function (terrainProvider) {      if (this._terrainProvider === terrainProvider) {        return;      }      //>>includeStart('debug', pragmas.debug);      if (!defined(terrainProvider)) {        throw new DeveloperError("terrainProvider is required.");      }      //>>includeEnd('debug');      this._terrainProvider = terrainProvider;      if (defined(this._quadtree)) {        this._quadtree.invalidateAllTiles();      }    },  },  /**   * The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset.   *   * @type {ClippingPlaneCollection}   *   * @private   */  clippingPlanes: {    get: function () {      return this._clippingPlanes;    },    set: function (value) {      ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");    },  },});function sortTileImageryByLayerIndex(a, b) {  let aImagery = a.loadingImagery;  if (!defined(aImagery)) {    aImagery = a.readyImagery;  }  let bImagery = b.loadingImagery;  if (!defined(bImagery)) {    bImagery = b.readyImagery;  }  return aImagery.imageryLayer._layerIndex - bImagery.imageryLayer._layerIndex;}/** * Make updates to the tile provider that are not involved in rendering. Called before the render update cycle. */GlobeSurfaceTileProvider.prototype.update = function (frameState) {  // update collection: imagery indices, base layers, raise layer show/hide event  this._imageryLayers._update();};function updateCredits(surface, frameState) {  const creditDisplay = frameState.creditDisplay;  if (    surface._terrainProvider.ready &&    defined(surface._terrainProvider.credit)  ) {    creditDisplay.addCredit(surface._terrainProvider.credit);  }  const imageryLayers = surface._imageryLayers;  for (let i = 0, len = imageryLayers.length; i < len; ++i) {    const imageryProvider = imageryLayers.get(i).imageryProvider;    if (imageryProvider.ready && defined(imageryProvider.credit)) {      creditDisplay.addCredit(imageryProvider.credit);    }  }}/** * Called at the beginning of each render frame, before {@link QuadtreeTileProvider#showTileThisFrame} * @param {FrameState} frameState The frame state. */GlobeSurfaceTileProvider.prototype.initialize = function (frameState) {  // update each layer for texture reprojection.  this._imageryLayers.queueReprojectionCommands(frameState);  if (this._layerOrderChanged) {    this._layerOrderChanged = false;    // Sort the TileImagery instances in each tile by the layer index.    this._quadtree.forEachLoadedTile(function (tile) {      tile.data.imagery.sort(sortTileImageryByLayerIndex);    });  }  // Add credits for terrain and imagery providers.  updateCredits(this, frameState);  const vertexArraysToDestroy = this._vertexArraysToDestroy;  const length = vertexArraysToDestroy.length;  for (let j = 0; j < length; ++j) {    GlobeSurfaceTile._freeVertexArray(vertexArraysToDestroy[j]);  }  vertexArraysToDestroy.length = 0;};/** * Called at the beginning of the update cycle for each render frame, before {@link QuadtreeTileProvider#showTileThisFrame} * or any other functions. * * @param {FrameState} frameState The frame state. */GlobeSurfaceTileProvider.prototype.beginUpdate = function (frameState) {  const tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;  for (let i = 0, len = tilesToRenderByTextureCount.length; i < len; ++i) {    const tiles = tilesToRenderByTextureCount[i];    if (defined(tiles)) {      tiles.length = 0;    }  }  // update clipping planes  const clippingPlanes = this._clippingPlanes;  if (defined(clippingPlanes) && clippingPlanes.enabled) {    clippingPlanes.update(frameState);  }  this._usedDrawCommands = 0;  this._hasLoadedTilesThisFrame = false;  this._hasFillTilesThisFrame = false;};/** * Called at the end of the update cycle for each render frame, after {@link QuadtreeTileProvider#showTileThisFrame} * and any other functions. * * @param {FrameState} frameState The frame state. */GlobeSurfaceTileProvider.prototype.endUpdate = function (frameState) {  if (!defined(this._renderState)) {    this._renderState = RenderState.fromCache({      // Write color and depth      cull: {        enabled: true,      },      depthTest: {        enabled: true,        func: DepthFunction.LESS,      },    });    this._blendRenderState = RenderState.fromCache({      // Write color and depth      cull: {        enabled: true,      },      depthTest: {        enabled: true,        func: DepthFunction.LESS_OR_EQUAL,      },      blending: BlendingState.ALPHA_BLEND,    });    let rs = clone(this._renderState, true);    rs.cull.enabled = false;    this._disableCullingRenderState = RenderState.fromCache(rs);    rs = clone(this._blendRenderState, true);    rs.cull.enabled = false;    this._disableCullingBlendRenderState = RenderState.fromCache(rs);  }  // If this frame has a mix of loaded and fill tiles, we need to propagate  // loaded heights to the fill tiles.  if (this._hasFillTilesThisFrame && this._hasLoadedTilesThisFrame) {    TerrainFillMesh.updateFillTiles(      this,      this._quadtree._tilesToRender,      frameState,      this._vertexArraysToDestroy    );  }  // When terrain exaggeration changes, all of the loaded tiles need to generate  // geodetic surface normals so they can scale properly when rendered.  // When exaggeration is reset, geodetic surface normals are removed to decrease  // memory usage. Some tiles might have been constructed with the correct  // exaggeration already, so skip over them.  // If the geodetic surface normals can't be created because the tile doesn't  // have a mesh, keep checking until the tile does have a mesh. This can happen  // if the tile's mesh starts construction in a worker thread right before the  // exaggeration changes.  const quadtree = this.quadtree;  const exaggeration = frameState.terrainExaggeration;  const exaggerationRelativeHeight =    frameState.terrainExaggerationRelativeHeight;  const exaggerationChanged =    this._oldTerrainExaggeration !== exaggeration ||    this._oldTerrainExaggerationRelativeHeight !== exaggerationRelativeHeight;  // Keep track of the next time there is a change in exaggeration  this._oldTerrainExaggeration = exaggeration;  this._oldTerrainExaggerationRelativeHeight = exaggerationRelativeHeight;  if (exaggerationChanged) {    quadtree.forEachLoadedTile(function (tile) {      const surfaceTile = tile.data;      surfaceTile.updateExaggeration(tile, frameState, quadtree);    });  }  // Add the tile render commands to the command list, sorted by texture count.  const tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;  for (    let textureCountIndex = 0,      textureCountLength = tilesToRenderByTextureCount.length;    textureCountIndex < textureCountLength;    ++textureCountIndex  ) {    const tilesToRender = tilesToRenderByTextureCount[textureCountIndex];    if (!defined(tilesToRender)) {      continue;    }    for (      let tileIndex = 0, tileLength = tilesToRender.length;      tileIndex < tileLength;      ++tileIndex    ) {      const tile = tilesToRender[tileIndex];      const tileBoundingRegion = tile.data.tileBoundingRegion;      addDrawCommandsForTile(this, tile, frameState);      frameState.minimumTerrainHeight = Math.min(        frameState.minimumTerrainHeight,        tileBoundingRegion.minimumHeight      );    }  }};function pushCommand(command, frameState) {  const globeTranslucencyState = frameState.globeTranslucencyState;  if (globeTranslucencyState.translucent) {    const isBlendCommand = command.renderState.blending.enabled;    globeTranslucencyState.pushDerivedCommands(      command,      isBlendCommand,      frameState    );  } else {    frameState.commandList.push(command);  }}/** * Adds draw commands for tiles rendered in the previous frame for a pick pass. * * @param {FrameState} frameState The frame state. */GlobeSurfaceTileProvider.prototype.updateForPick = function (frameState) {  // Add the tile pick commands from the tiles drawn last frame.  const drawCommands = this._drawCommands;  for (let i = 0, length = this._usedDrawCommands; i < length; ++i) {    pushCommand(drawCommands[i], frameState);  }};/** * Cancels any imagery re-projections in the queue. */GlobeSurfaceTileProvider.prototype.cancelReprojections = function () {  this._imageryLayers.cancelReprojections();};/** * Gets the maximum geometric error allowed in a tile at a given level, in meters.  This function should not be * called before {@link GlobeSurfaceTileProvider#ready} returns true. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error in meters. */GlobeSurfaceTileProvider.prototype.getLevelMaximumGeometricError = function (  level) {  return this._terrainProvider.getLevelMaximumGeometricError(level);};/** * Loads, or continues loading, a given tile.  This function will continue to be called * until {@link QuadtreeTile#state} is no longer {@link QuadtreeTileLoadState#LOADING}.  This function should * not be called before {@link GlobeSurfaceTileProvider#ready} returns true. * * @param {FrameState} frameState The frame state. * @param {QuadtreeTile} tile The tile to load. * * @exception {DeveloperError} <code>loadTile</code> must not be called before the tile provider is ready. */GlobeSurfaceTileProvider.prototype.loadTile = function (frameState, tile) {  // We don't want to load imagery until we're certain that the terrain tiles are actually visible.  // So if our bounding volume isn't accurate because it came from another tile, load terrain only  // initially. If we load some terrain and suddenly have a more accurate bounding volume and the  // tile is _still_ visible, give the tile a chance to load imagery immediately rather than  // waiting for next frame.  let surfaceTile = tile.data;  let terrainOnly = true;  let terrainStateBefore;  if (defined(surfaceTile)) {    terrainOnly =      surfaceTile.boundingVolumeSourceTile !== tile ||      tile._lastSelectionResult === TileSelectionResult.CULLED_BUT_NEEDED;    terrainStateBefore = surfaceTile.terrainState;  }  GlobeSurfaceTile.processStateMachine(    tile,    frameState,    this.terrainProvider,    this._imageryLayers,    this.quadtree,    this._vertexArraysToDestroy,    terrainOnly  );  surfaceTile = tile.data;  if (terrainOnly && terrainStateBefore !== tile.data.terrainState) {    // Terrain state changed. If:    // a) The tile is visible, and    // b) The bounding volume is accurate (updated as a side effect of computing visibility)    // Then we'll load imagery, too.    if (      this.computeTileVisibility(tile, frameState, this.quadtree.occluders) !==        Visibility.NONE &&      surfaceTile.boundingVolumeSourceTile === tile    ) {      terrainOnly = false;      GlobeSurfaceTile.processStateMachine(        tile,        frameState,        this.terrainProvider,        this._imageryLayers,        this.quadtree,        this._vertexArraysToDestroy,        terrainOnly      );    }  }};const boundingSphereScratch = new BoundingSphere();const rectangleIntersectionScratch = new Rectangle();const splitCartographicLimitRectangleScratch = new Rectangle();const rectangleCenterScratch = new Cartographic();// cartographicLimitRectangle may span the IDL, but tiles never will.function clipRectangleAntimeridian(tileRectangle, cartographicLimitRectangle) {  if (cartographicLimitRectangle.west < cartographicLimitRectangle.east) {    return cartographicLimitRectangle;  }  const splitRectangle = Rectangle.clone(    cartographicLimitRectangle,    splitCartographicLimitRectangleScratch  );  const tileCenter = Rectangle.center(tileRectangle, rectangleCenterScratch);  if (tileCenter.longitude > 0.0) {    splitRectangle.east = CesiumMath.PI;  } else {    splitRectangle.west = -CesiumMath.PI;  }  return splitRectangle;}function isUndergroundVisible(tileProvider, frameState) {  if (frameState.cameraUnderground) {    return true;  }  if (frameState.globeTranslucencyState.translucent) {    return true;  }  if (tileProvider.backFaceCulling) {    return false;  }  const clippingPlanes = tileProvider._clippingPlanes;  if (defined(clippingPlanes) && clippingPlanes.enabled) {    return true;  }  if (    !Rectangle.equals(      tileProvider.cartographicLimitRectangle,      Rectangle.MAX_VALUE    )  ) {    return true;  }  return false;}/** * Determines the visibility of a given tile.  The tile may be fully visible, partially visible, or not * visible at all.  Tiles that are renderable and are at least partially visible will be shown by a call * to {@link GlobeSurfaceTileProvider#showTileThisFrame}. * * @param {QuadtreeTile} tile The tile instance. * @param {FrameState} frameState The state information about the current frame. * @param {QuadtreeOccluders} occluders The objects that may occlude this tile. * * @returns {Visibility} Visibility.NONE if the tile is not visible, *                       Visibility.PARTIAL if the tile is partially visible, or *                       Visibility.FULL if the tile is fully visible. */GlobeSurfaceTileProvider.prototype.computeTileVisibility = function (  tile,  frameState,  occluders) {  const distance = this.computeDistanceToTile(tile, frameState);  tile._distance = distance;  const undergroundVisible = isUndergroundVisible(this, frameState);  if (frameState.fog.enabled && !undergroundVisible) {    if (CesiumMath.fog(distance, frameState.fog.density) >= 1.0) {      // Tile is completely in fog so return that it is not visible.      return Visibility.NONE;    }  }  const surfaceTile = tile.data;  const tileBoundingRegion = surfaceTile.tileBoundingRegion;  if (surfaceTile.boundingVolumeSourceTile === undefined) {    // We have no idea where this tile is, so let's just call it partially visible.    return Visibility.PARTIAL;  }  const cullingVolume = frameState.cullingVolume;  let boundingVolume = tileBoundingRegion.boundingVolume;  if (!defined(boundingVolume)) {    boundingVolume = tileBoundingRegion.boundingSphere;  }  // Check if the tile is outside the limit area in cartographic space  surfaceTile.clippedByBoundaries = false;  const clippedCartographicLimitRectangle = clipRectangleAntimeridian(    tile.rectangle,    this.cartographicLimitRectangle  );  const areaLimitIntersection = Rectangle.simpleIntersection(    clippedCartographicLimitRectangle,    tile.rectangle,    rectangleIntersectionScratch  );  if (!defined(areaLimitIntersection)) {    return Visibility.NONE;  }  if (!Rectangle.equals(areaLimitIntersection, tile.rectangle)) {    surfaceTile.clippedByBoundaries = true;  }  if (frameState.mode !== SceneMode.SCENE3D) {    boundingVolume = boundingSphereScratch;    BoundingSphere.fromRectangleWithHeights2D(      tile.rectangle,      frameState.mapProjection,      tileBoundingRegion.minimumHeight,      tileBoundingRegion.maximumHeight,      boundingVolume    );    Cartesian3.fromElements(      boundingVolume.center.z,      boundingVolume.center.x,      boundingVolume.center.y,      boundingVolume.center    );    if (      frameState.mode === SceneMode.MORPHING &&      defined(surfaceTile.renderedMesh)    ) {      boundingVolume = BoundingSphere.union(        tileBoundingRegion.boundingSphere,        boundingVolume,        boundingVolume      );    }  }  if (!defined(boundingVolume)) {    return Visibility.PARTIAL;  }  const clippingPlanes = this._clippingPlanes;  if (defined(clippingPlanes) && clippingPlanes.enabled) {    const planeIntersection = clippingPlanes.computeIntersectionWithBoundingVolume(      boundingVolume    );    tile.isClipped = planeIntersection !== Intersect.INSIDE;    if (planeIntersection === Intersect.OUTSIDE) {      return Visibility.NONE;    }  }  let visibility;  const intersection = cullingVolume.computeVisibility(boundingVolume);  if (intersection === Intersect.OUTSIDE) {    visibility = Visibility.NONE;  } else if (intersection === Intersect.INTERSECTING) {    visibility = Visibility.PARTIAL;  } else if (intersection === Intersect.INSIDE) {    visibility = Visibility.FULL;  }  if (visibility === Visibility.NONE) {    return visibility;  }  const ortho3D =    frameState.mode === SceneMode.SCENE3D &&    frameState.camera.frustum instanceof OrthographicFrustum;  if (    frameState.mode === SceneMode.SCENE3D &&    !ortho3D &&    defined(occluders) &&    !undergroundVisible  ) {    const occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace;    if (!defined(occludeePointInScaledSpace)) {      return visibility;    }    if (      occluders.ellipsoid.isScaledSpacePointVisiblePossiblyUnderEllipsoid(        occludeePointInScaledSpace,        tileBoundingRegion.minimumHeight      )    ) {      return visibility;    }    return Visibility.NONE;  }  return visibility;};/** * Determines if the given tile can be refined * @param {QuadtreeTile} tile The tile to check. * @returns {boolean} True if the tile can be refined, false if it cannot. */GlobeSurfaceTileProvider.prototype.canRefine = function (tile) {  // Only allow refinement it we know whether or not the children of this tile exist.  // For a tileset with `availability`, we'll always be able to refine.  // We can ask for availability of _any_ child tile because we only need to confirm  // that we get a yes or no answer, it doesn't matter what the answer is.  if (defined(tile.data.terrainData)) {    return true;  }  const childAvailable = this.terrainProvider.getTileDataAvailable(    tile.x * 2,    tile.y * 2,    tile.level + 1  );  return childAvailable !== undefined;};const readyImageryScratch = [];const canRenderTraversalStack = [];/** * Determines if the given not-fully-loaded tile can be rendered without losing detail that * was present last frame as a result of rendering descendant tiles. This method will only be * called if this tile's descendants were rendered last frame. If the tile is fully loaded, * it is assumed that this method will return true and it will not be called. * @param {QuadtreeTile} tile The tile to check. * @returns {boolean} True if the tile can be rendered without losing detail. */GlobeSurfaceTileProvider.prototype.canRenderWithoutLosingDetail = function (  tile,  frameState) {  const surfaceTile = tile.data;  const readyImagery = readyImageryScratch;  readyImagery.length = this._imageryLayers.length;  let terrainReady = false;  let initialImageryState = false;  let imagery;  if (defined(surfaceTile)) {    // We can render even with non-ready terrain as long as all our rendered descendants    // are missing terrain geometry too. i.e. if we rendered fills for more detailed tiles    // last frame, it's ok to render a fill for this tile this frame.    terrainReady = surfaceTile.terrainState === TerrainState.READY;    // Initially assume all imagery layers are ready, unless imagery hasn't been initialized at all.    initialImageryState = true;    imagery = surfaceTile.imagery;  }  let i;  let len;  for (i = 0, len = readyImagery.length; i < len; ++i) {    readyImagery[i] = initialImageryState;  }  if (defined(imagery)) {    for (i = 0, len = imagery.length; i < len; ++i) {      const tileImagery = imagery[i];      const loadingImagery = tileImagery.loadingImagery;      const isReady =        !defined(loadingImagery) ||        loadingImagery.state === ImageryState.FAILED ||        loadingImagery.state === ImageryState.INVALID;      const layerIndex = (        tileImagery.loadingImagery || tileImagery.readyImagery      ).imageryLayer._layerIndex;      // For a layer to be ready, all tiles belonging to that layer must be ready.      readyImagery[layerIndex] = isReady && readyImagery[layerIndex];    }  }  const lastFrame = this.quadtree._lastSelectionFrameNumber;  // Traverse the descendants looking for one with terrain or imagery that is not loaded on this tile.  const stack = canRenderTraversalStack;  stack.length = 0;  stack.push(    tile.southwestChild,    tile.southeastChild,    tile.northwestChild,    tile.northeastChild  );  while (stack.length > 0) {    const descendant = stack.pop();    const lastFrameSelectionResult =      descendant._lastSelectionResultFrame === lastFrame        ? descendant._lastSelectionResult        : TileSelectionResult.NONE;    if (lastFrameSelectionResult === TileSelectionResult.RENDERED) {      const descendantSurface = descendant.data;      if (!defined(descendantSurface)) {        // Descendant has no data, so it can't block rendering.        continue;      }      if (        !terrainReady &&        descendant.data.terrainState === TerrainState.READY      ) {        // Rendered descendant has real terrain, but we don't. Rendering is blocked.        return false;      }      const descendantImagery = descendant.data.imagery;      for (i = 0, len = descendantImagery.length; i < len; ++i) {        const descendantTileImagery = descendantImagery[i];        const descendantLoadingImagery = descendantTileImagery.loadingImagery;        const descendantIsReady =          !defined(descendantLoadingImagery) ||          descendantLoadingImagery.state === ImageryState.FAILED ||          descendantLoadingImagery.state === ImageryState.INVALID;        const descendantLayerIndex = (          descendantTileImagery.loadingImagery ||          descendantTileImagery.readyImagery        ).imageryLayer._layerIndex;        // If this imagery tile of a descendant is ready but the layer isn't ready in this tile,        // then rendering is blocked.        if (descendantIsReady && !readyImagery[descendantLayerIndex]) {          return false;        }      }    } else if (lastFrameSelectionResult === TileSelectionResult.REFINED) {      stack.push(        descendant.southwestChild,        descendant.southeastChild,        descendant.northwestChild,        descendant.northeastChild      );    }  }  return true;};const tileDirectionScratch = new Cartesian3();/** * Determines the priority for loading this tile. Lower priority values load sooner. * @param {QuadtreeTile} tile The tile. * @param {FrameState} frameState The frame state. * @returns {Number} The load priority value. */GlobeSurfaceTileProvider.prototype.computeTileLoadPriority = function (  tile,  frameState) {  const surfaceTile = tile.data;  if (surfaceTile === undefined) {    return 0.0;  }  const obb = surfaceTile.tileBoundingRegion.boundingVolume;  if (obb === undefined) {    return 0.0;  }  const cameraPosition = frameState.camera.positionWC;  const cameraDirection = frameState.camera.directionWC;  const tileDirection = Cartesian3.subtract(    obb.center,    cameraPosition,    tileDirectionScratch  );  const magnitude = Cartesian3.magnitude(tileDirection);  if (magnitude < CesiumMath.EPSILON5) {    return 0.0;  }  Cartesian3.divideByScalar(tileDirection, magnitude, tileDirection);  return (    (1.0 - Cartesian3.dot(tileDirection, cameraDirection)) * tile._distance  );};const modifiedModelViewScratch = new Matrix4();const modifiedModelViewProjectionScratch = new Matrix4();const tileRectangleScratch = new Cartesian4();const localizedCartographicLimitRectangleScratch = new Cartesian4();const localizedTranslucencyRectangleScratch = new Cartesian4();const rtcScratch = new Cartesian3();const centerEyeScratch = new Cartesian3();const southwestScratch = new Cartesian3();const northeastScratch = new Cartesian3();/** * Shows a specified tile in this frame.  The provider can cause the tile to be shown by adding * render commands to the commandList, or use any other method as appropriate.  The tile is not * expected to be visible next frame as well, unless this method is called next frame, too. * * @param {QuadtreeTile} tile The tile instance. * @param {FrameState} frameState The state information of the current rendering frame. */GlobeSurfaceTileProvider.prototype.showTileThisFrame = function (  tile,  frameState) {  let readyTextureCount = 0;  const tileImageryCollection = tile.data.imagery;  for (let i = 0, len = tileImageryCollection.length; i < len; ++i) {    const tileImagery = tileImageryCollection[i];    if (      defined(tileImagery.readyImagery) &&      tileImagery.readyImagery.imageryLayer.alpha !== 0.0    ) {      ++readyTextureCount;    }  }  let tileSet = this._tilesToRenderByTextureCount[readyTextureCount];  if (!defined(tileSet)) {    tileSet = [];    this._tilesToRenderByTextureCount[readyTextureCount] = tileSet;  }  tileSet.push(tile);  const surfaceTile = tile.data;  if (!defined(surfaceTile.vertexArray)) {    this._hasFillTilesThisFrame = true;  } else {    this._hasLoadedTilesThisFrame = true;  }  const debug = this._debug;  ++debug.tilesRendered;  debug.texturesRendered += readyTextureCount;};const cornerPositionsScratch = [  new Cartesian3(),  new Cartesian3(),  new Cartesian3(),  new Cartesian3(),];function computeOccludeePoint(  tileProvider,  center,  rectangle,  minimumHeight,  maximumHeight,  result) {  const ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;  const ellipsoid = ellipsoidalOccluder.ellipsoid;  const cornerPositions = cornerPositionsScratch;  Cartesian3.fromRadians(    rectangle.west,    rectangle.south,    maximumHeight,    ellipsoid,    cornerPositions[0]  );  Cartesian3.fromRadians(    rectangle.east,    rectangle.south,    maximumHeight,    ellipsoid,    cornerPositions[1]  );  Cartesian3.fromRadians(    rectangle.west,    rectangle.north,    maximumHeight,    ellipsoid,    cornerPositions[2]  );  Cartesian3.fromRadians(    rectangle.east,    rectangle.north,    maximumHeight,    ellipsoid,    cornerPositions[3]  );  return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(    center,    cornerPositions,    minimumHeight,    result  );}/** * Gets the distance from the camera to the closest point on the tile.  This is used for level-of-detail selection. * * @param {QuadtreeTile} tile The tile instance. * @param {FrameState} frameState The state information of the current rendering frame. * * @returns {Number} The distance from the camera to the closest point on the tile, in meters. */GlobeSurfaceTileProvider.prototype.computeDistanceToTile = function (  tile,  frameState) {  // The distance should be:  // 1. the actual distance to the tight-fitting bounding volume, or  // 2. a distance that is equal to or greater than the actual distance to the tight-fitting bounding volume.  //  // When we don't know the min/max heights for a tile, but we do know the min/max of an ancestor tile, we can  // build a tight-fitting bounding volume horizontally, but not vertically. The min/max heights from the  // ancestor will likely form a volume that is much bigger than it needs to be. This means that the volume may  // be deemed to be much closer to the camera than it really is, causing us to select tiles that are too detailed.  // Loading too-detailed tiles is super expensive, so we don't want to do that. We don't know where the child  // tile really lies within the parent range of heights, but we _do_ know the child tile can't be any closer than  // the ancestor height surface (min or max) that is _farthest away_ from the camera. So if we compute distance  // based on that conservative metric, we may end up loading tiles that are not detailed enough, but that's much  // better (faster) than loading tiles that are too detailed.  updateTileBoundingRegion(tile, this, frameState);  const surfaceTile = tile.data;  const boundingVolumeSourceTile = surfaceTile.boundingVolumeSourceTile;  if (boundingVolumeSourceTile === undefined) {    // Can't find any min/max heights anywhere? Ok, let's just say the    // tile is really far away so we'll load and render it rather than    // refining.    return 9999999999.0;  }  const tileBoundingRegion = surfaceTile.tileBoundingRegion;  const min = tileBoundingRegion.minimumHeight;  const max = tileBoundingRegion.maximumHeight;  if (surfaceTile.boundingVolumeSourceTile !== tile) {    const cameraHeight = frameState.camera.positionCartographic.height;    const distanceToMin = Math.abs(cameraHeight - min);    const distanceToMax = Math.abs(cameraHeight - max);    if (distanceToMin > distanceToMax) {      tileBoundingRegion.minimumHeight = min;      tileBoundingRegion.maximumHeight = min;    } else {      tileBoundingRegion.minimumHeight = max;      tileBoundingRegion.maximumHeight = max;    }  }  const result = tileBoundingRegion.distanceToCamera(frameState);  tileBoundingRegion.minimumHeight = min;  tileBoundingRegion.maximumHeight = max;  return result;};function updateTileBoundingRegion(tile, tileProvider, frameState) {  let surfaceTile = tile.data;  if (surfaceTile === undefined) {    surfaceTile = tile.data = new GlobeSurfaceTile();  }  const ellipsoid = tile.tilingScheme.ellipsoid;  if (surfaceTile.tileBoundingRegion === undefined) {    surfaceTile.tileBoundingRegion = new TileBoundingRegion({      computeBoundingVolumes: false,      rectangle: tile.rectangle,      ellipsoid: ellipsoid,      minimumHeight: 0.0,      maximumHeight: 0.0,    });  }  const tileBoundingRegion = surfaceTile.tileBoundingRegion;  const oldMinimumHeight = tileBoundingRegion.minimumHeight;  const oldMaximumHeight = tileBoundingRegion.maximumHeight;  let hasBoundingVolumesFromMesh = false;  let sourceTile = tile;  // Get min and max heights from the mesh.  // If the mesh is not available, get them from the terrain data.  // If the terrain data is not available either, get them from an ancestor.  // If none of the ancestors are available, then there are no min and max heights for this tile at this time.  const mesh = surfaceTile.mesh;  const terrainData = surfaceTile.terrainData;  if (    mesh !== undefined &&    mesh.minimumHeight !== undefined &&    mesh.maximumHeight !== undefined  ) {    tileBoundingRegion.minimumHeight = mesh.minimumHeight;    tileBoundingRegion.maximumHeight = mesh.maximumHeight;    hasBoundingVolumesFromMesh = true;  } else if (    terrainData !== undefined &&    terrainData._minimumHeight !== undefined &&    terrainData._maximumHeight !== undefined  ) {    tileBoundingRegion.minimumHeight = terrainData._minimumHeight;    tileBoundingRegion.maximumHeight = terrainData._maximumHeight;  } else {    // No accurate min/max heights available, so we're stuck with min/max heights from an ancestor tile.    tileBoundingRegion.minimumHeight = Number.NaN;    tileBoundingRegion.maximumHeight = Number.NaN;    let ancestorTile = tile.parent;    while (ancestorTile !== undefined) {      const ancestorSurfaceTile = ancestorTile.data;      if (ancestorSurfaceTile !== undefined) {        const ancestorMesh = ancestorSurfaceTile.mesh;        const ancestorTerrainData = ancestorSurfaceTile.terrainData;        if (          ancestorMesh !== undefined &&          ancestorMesh.minimumHeight !== undefined &&          ancestorMesh.maximumHeight !== undefined        ) {          tileBoundingRegion.minimumHeight = ancestorMesh.minimumHeight;          tileBoundingRegion.maximumHeight = ancestorMesh.maximumHeight;          break;        } else if (          ancestorTerrainData !== undefined &&          ancestorTerrainData._minimumHeight !== undefined &&          ancestorTerrainData._maximumHeight !== undefined        ) {          tileBoundingRegion.minimumHeight = ancestorTerrainData._minimumHeight;          tileBoundingRegion.maximumHeight = ancestorTerrainData._maximumHeight;          break;        }      }      ancestorTile = ancestorTile.parent;    }    sourceTile = ancestorTile;  }  // Update bounding regions from the min and max heights  if (sourceTile !== undefined) {    const exaggeration = frameState.terrainExaggeration;    const exaggerationRelativeHeight =      frameState.terrainExaggerationRelativeHeight;    const hasExaggeration = exaggeration !== 1.0;    if (hasExaggeration) {      hasBoundingVolumesFromMesh = false;      tileBoundingRegion.minimumHeight = TerrainExaggeration.getHeight(        tileBoundingRegion.minimumHeight,        exaggeration,        exaggerationRelativeHeight      );      tileBoundingRegion.maximumHeight = TerrainExaggeration.getHeight(        tileBoundingRegion.maximumHeight,        exaggeration,        exaggerationRelativeHeight      );    }    if (hasBoundingVolumesFromMesh) {      if (!surfaceTile.boundingVolumeIsFromMesh) {        tileBoundingRegion._orientedBoundingBox = OrientedBoundingBox.clone(          mesh.orientedBoundingBox,          tileBoundingRegion._orientedBoundingBox        );        tileBoundingRegion._boundingSphere = BoundingSphere.clone(          mesh.boundingSphere3D,          tileBoundingRegion._boundingSphere        );        surfaceTile.occludeePointInScaledSpace = Cartesian3.clone(          mesh.occludeePointInScaledSpace,          surfaceTile.occludeePointInScaledSpace        );        // If the occludee point is not defined, fallback to calculating it from the OBB        if (!defined(surfaceTile.occludeePointInScaledSpace)) {          surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(            tileProvider,            tileBoundingRegion._orientedBoundingBox.center,            tile.rectangle,            tileBoundingRegion.minimumHeight,            tileBoundingRegion.maximumHeight,            surfaceTile.occludeePointInScaledSpace          );        }      }    } else {      const needsBounds =        tileBoundingRegion._orientedBoundingBox === undefined ||        tileBoundingRegion._boundingSphere === undefined;      const heightChanged =        tileBoundingRegion.minimumHeight !== oldMinimumHeight ||        tileBoundingRegion.maximumHeight !== oldMaximumHeight;      if (heightChanged || needsBounds) {        // Bounding volumes need to be recomputed in some circumstances        tileBoundingRegion.computeBoundingVolumes(ellipsoid);        surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(          tileProvider,          tileBoundingRegion._orientedBoundingBox.center,          tile.rectangle,          tileBoundingRegion.minimumHeight,          tileBoundingRegion.maximumHeight,          surfaceTile.occludeePointInScaledSpace        );      }    }    surfaceTile.boundingVolumeSourceTile = sourceTile;    surfaceTile.boundingVolumeIsFromMesh = hasBoundingVolumesFromMesh;  } else {    surfaceTile.boundingVolumeSourceTile = undefined;    surfaceTile.boundingVolumeIsFromMesh = false;  }}/** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see GlobeSurfaceTileProvider#destroy */GlobeSurfaceTileProvider.prototype.isDestroyed = function () {  return false;};/** * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <br /><br /> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * provider = provider && provider(); * * @see GlobeSurfaceTileProvider#isDestroyed */GlobeSurfaceTileProvider.prototype.destroy = function () {  this._tileProvider = this._tileProvider && this._tileProvider.destroy();  this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy();  return destroyObject(this);};function getTileReadyCallback(tileImageriesToFree, layer, terrainProvider) {  return function (tile) {    let tileImagery;    let imagery;    let startIndex = -1;    const tileImageryCollection = tile.data.imagery;    const length = tileImageryCollection.length;    let i;    for (i = 0; i < length; ++i) {      tileImagery = tileImageryCollection[i];      imagery = defaultValue(        tileImagery.readyImagery,        tileImagery.loadingImagery      );      if (imagery.imageryLayer === layer) {        startIndex = i;        break;      }    }    if (startIndex !== -1) {      const endIndex = startIndex + tileImageriesToFree;      tileImagery = tileImageryCollection[endIndex];      imagery = defined(tileImagery)        ? defaultValue(tileImagery.readyImagery, tileImagery.loadingImagery)        : undefined;      if (!defined(imagery) || imagery.imageryLayer !== layer) {        // Return false to keep the callback if we have to wait on the skeletons        // Return true to remove the callback if something went wrong        return !layer._createTileImagerySkeletons(          tile,          terrainProvider,          endIndex        );      }      for (i = startIndex; i < endIndex; ++i) {        tileImageryCollection[i].freeResources();      }      tileImageryCollection.splice(startIndex, tileImageriesToFree);    }    return true; // Everything is done, so remove the callback  };}GlobeSurfaceTileProvider.prototype._onLayerAdded = function (layer, index) {  if (layer.show) {    const terrainProvider = this._terrainProvider;    const that = this;    const imageryProvider = layer.imageryProvider;    const tileImageryUpdatedEvent = this._imageryLayersUpdatedEvent;    imageryProvider._reload = function () {      // Clear the layer's cache      layer._imageryCache = {};      that._quadtree.forEachLoadedTile(function (tile) {        // If this layer is still waiting to for the loaded callback, just return        if (defined(tile._loadedCallbacks[layer._layerIndex])) {          return;        }        let i;        // Figure out how many TileImageries we will need to remove and where to insert new ones        const tileImageryCollection = tile.data.imagery;        const length = tileImageryCollection.length;        let startIndex = -1;        let tileImageriesToFree = 0;        for (i = 0; i < length; ++i) {          const tileImagery = tileImageryCollection[i];          const imagery = defaultValue(            tileImagery.readyImagery,            tileImagery.loadingImagery          );          if (imagery.imageryLayer === layer) {            if (startIndex === -1) {              startIndex = i;            }            ++tileImageriesToFree;          } else if (startIndex !== -1) {            // iterated past the section of TileImageries belonging to this layer, no need to continue.            break;          }        }        if (startIndex === -1) {          return;        }        // Insert immediately after existing TileImageries        const insertionPoint = startIndex + tileImageriesToFree;        // Create new TileImageries for all loaded tiles        if (          layer._createTileImagerySkeletons(            tile,            terrainProvider,            insertionPoint          )        ) {          // Add callback to remove old TileImageries when the new TileImageries are ready          tile._loadedCallbacks[layer._layerIndex] = getTileReadyCallback(            tileImageriesToFree,            layer,            terrainProvider          );          tile.state = QuadtreeTileLoadState.LOADING;        }      });    };    // create TileImageries for this layer for all previously loaded tiles    this._quadtree.forEachLoadedTile(function (tile) {      if (layer._createTileImagerySkeletons(tile, terrainProvider)) {        tile.state = QuadtreeTileLoadState.LOADING;        // Tiles that are not currently being rendered need to load the new layer before they're renderable.        // We don't mark the rendered tiles non-renderable, though, because that would make the globe disappear.        if (          tile.level !== 0 &&          (tile._lastSelectionResultFrame !==            that.quadtree._lastSelectionFrameNumber ||            tile._lastSelectionResult !== TileSelectionResult.RENDERED)        ) {          tile.renderable = false;        }      }    });    this._layerOrderChanged = true;    tileImageryUpdatedEvent.raiseEvent();  }};GlobeSurfaceTileProvider.prototype._onLayerRemoved = function (layer, index) {  // destroy TileImagerys for this layer for all previously loaded tiles  this._quadtree.forEachLoadedTile(function (tile) {    const tileImageryCollection = tile.data.imagery;    let startIndex = -1;    let numDestroyed = 0;    for (let i = 0, len = tileImageryCollection.length; i < len; ++i) {      const tileImagery = tileImageryCollection[i];      let imagery = tileImagery.loadingImagery;      if (!defined(imagery)) {        imagery = tileImagery.readyImagery;      }      if (imagery.imageryLayer === layer) {        if (startIndex === -1) {          startIndex = i;        }        tileImagery.freeResources();        ++numDestroyed;      } else if (startIndex !== -1) {        // iterated past the section of TileImagerys belonging to this layer, no need to continue.        break;      }    }    if (startIndex !== -1) {      tileImageryCollection.splice(startIndex, numDestroyed);    }  });  if (defined(layer.imageryProvider)) {    layer.imageryProvider._reload = undefined;  }  this._imageryLayersUpdatedEvent.raiseEvent();};GlobeSurfaceTileProvider.prototype._onLayerMoved = function (  layer,  newIndex,  oldIndex) {  this._layerOrderChanged = true;  this._imageryLayersUpdatedEvent.raiseEvent();};GlobeSurfaceTileProvider.prototype._onLayerShownOrHidden = function (  layer,  index,  show) {  if (show) {    this._onLayerAdded(layer, index);  } else {    this._onLayerRemoved(layer, index);  }};const scratchClippingPlanesMatrix = new Matrix4();const scratchInverseTransposeClippingPlanesMatrix = new Matrix4();function createTileUniformMap(frameState, globeSurfaceTileProvider) {  const uniformMap = {    u_initialColor: function () {      return this.properties.initialColor;    },    u_fillHighlightColor: function () {      return this.properties.fillHighlightColor;    },    u_zoomedOutOceanSpecularIntensity: function () {      return this.properties.zoomedOutOceanSpecularIntensity;    },    u_oceanNormalMap: function () {      return this.properties.oceanNormalMap;    },    u_atmosphereLightIntensity: function () {      return this.properties.atmosphereLightIntensity;    },    u_atmosphereRayleighCoefficient: function () {      return this.properties.atmosphereRayleighCoefficient;    },    u_atmosphereMieCoefficient: function () {      return this.properties.atmosphereMieCoefficient;    },    u_atmosphereRayleighScaleHeight: function () {      return this.properties.atmosphereRayleighScaleHeight;    },    u_atmosphereMieScaleHeight: function () {      return this.properties.atmosphereMieScaleHeight;    },    u_atmosphereMieAnisotropy: function () {      return this.properties.atmosphereMieAnisotropy;    },    u_lightingFadeDistance: function () {      return this.properties.lightingFadeDistance;    },    u_nightFadeDistance: function () {      return this.properties.nightFadeDistance;    },    u_center3D: function () {      return this.properties.center3D;    },    u_terrainExaggerationAndRelativeHeight: function () {      return this.properties.terrainExaggerationAndRelativeHeight;    },    u_tileRectangle: function () {      return this.properties.tileRectangle;    },    u_modifiedModelView: function () {      const viewMatrix = frameState.context.uniformState.view;      const centerEye = Matrix4.multiplyByPoint(        viewMatrix,        this.properties.rtc,        centerEyeScratch      );      Matrix4.setTranslation(viewMatrix, centerEye, modifiedModelViewScratch);      return modifiedModelViewScratch;    },    u_modifiedModelViewProjection: function () {      const viewMatrix = frameState.context.uniformState.view;      const projectionMatrix = frameState.context.uniformState.projection;      const centerEye = Matrix4.multiplyByPoint(        viewMatrix,        this.properties.rtc,        centerEyeScratch      );      Matrix4.setTranslation(        viewMatrix,        centerEye,        modifiedModelViewProjectionScratch      );      Matrix4.multiply(        projectionMatrix,        modifiedModelViewProjectionScratch,        modifiedModelViewProjectionScratch      );      return modifiedModelViewProjectionScratch;    },    u_dayTextures: function () {      return this.properties.dayTextures;    },    u_dayTextureTranslationAndScale: function () {      return this.properties.dayTextureTranslationAndScale;    },    u_dayTextureTexCoordsRectangle: function () {      return this.properties.dayTextureTexCoordsRectangle;    },    u_dayTextureUseWebMercatorT: function () {      return this.properties.dayTextureUseWebMercatorT;    },    u_dayTextureAlpha: function () {      return this.properties.dayTextureAlpha;    },    u_dayTextureNightAlpha: function () {      return this.properties.dayTextureNightAlpha;    },    u_dayTextureDayAlpha: function () {      return this.properties.dayTextureDayAlpha;    },    u_dayTextureBrightness: function () {      return this.properties.dayTextureBrightness;    },    u_dayTextureContrast: function () {      return this.properties.dayTextureContrast;    },    u_dayTextureHue: function () {      return this.properties.dayTextureHue;    },    u_dayTextureSaturation: function () {      return this.properties.dayTextureSaturation;    },    u_dayTextureOneOverGamma: function () {      return this.properties.dayTextureOneOverGamma;    },    u_dayIntensity: function () {      return this.properties.dayIntensity;    },    u_southAndNorthLatitude: function () {      return this.properties.southAndNorthLatitude;    },    u_southMercatorYAndOneOverHeight: function () {      return this.properties.southMercatorYAndOneOverHeight;    },    u_waterMask: function () {      return this.properties.waterMask;    },    u_waterMaskTranslationAndScale: function () {      return this.properties.waterMaskTranslationAndScale;    },    u_minMaxHeight: function () {      return this.properties.minMaxHeight;    },    u_scaleAndBias: function () {      return this.properties.scaleAndBias;    },    u_dayTextureSplit: function () {      return this.properties.dayTextureSplit;    },    u_dayTextureCutoutRectangles: function () {      return this.properties.dayTextureCutoutRectangles;    },    u_clippingPlanes: function () {      const clippingPlanes = globeSurfaceTileProvider._clippingPlanes;      if (defined(clippingPlanes) && defined(clippingPlanes.texture)) {        // Check in case clippingPlanes hasn't been updated yet.        return clippingPlanes.texture;      }      return frameState.context.defaultTexture;    },    u_cartographicLimitRectangle: function () {      return this.properties.localizedCartographicLimitRectangle;    },    u_clippingPlanesMatrix: function () {      const clippingPlanes = globeSurfaceTileProvider._clippingPlanes;      const transform = defined(clippingPlanes)        ? Matrix4.multiply(            frameState.context.uniformState.view,            clippingPlanes.modelMatrix,            scratchClippingPlanesMatrix          )        : Matrix4.IDENTITY;      return Matrix4.inverseTranspose(        transform,        scratchInverseTransposeClippingPlanesMatrix      );    },    u_clippingPlanesEdgeStyle: function () {      const style = this.properties.clippingPlanesEdgeColor;      style.alpha = this.properties.clippingPlanesEdgeWidth;      return style;    },    u_minimumBrightness: function () {      return frameState.fog.minimumBrightness;    },    u_hsbShift: function () {      return this.properties.hsbShift;    },    u_colorsToAlpha: function () {      return this.properties.colorsToAlpha;    },    u_frontFaceAlphaByDistance: function () {      return this.properties.frontFaceAlphaByDistance;    },    u_backFaceAlphaByDistance: function () {      return this.properties.backFaceAlphaByDistance;    },    u_translucencyRectangle: function () {      return this.properties.localizedTranslucencyRectangle;    },    u_undergroundColor: function () {      return this.properties.undergroundColor;    },    u_undergroundColorAlphaByDistance: function () {      return this.properties.undergroundColorAlphaByDistance;    },    u_lambertDiffuseMultiplier: function () {      return this.properties.lambertDiffuseMultiplier;    },    // make a separate object so that changes to the properties are seen on    // derived commands that combine another uniform map with this one.    properties: {      initialColor: new Cartesian4(0.0, 0.0, 0.5, 1.0),      fillHighlightColor: new Color(0.0, 0.0, 0.0, 0.0),      zoomedOutOceanSpecularIntensity: 0.5,      oceanNormalMap: undefined,      lightingFadeDistance: new Cartesian2(6500000.0, 9000000.0),      nightFadeDistance: new Cartesian2(10000000.0, 40000000.0),      atmosphereLightIntensity: 10.0,      atmosphereRayleighCoefficient: new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6),      atmosphereMieCoefficient: new Cartesian3(21e-6, 21e-6, 21e-6),      atmosphereRayleighScaleHeight: 10000.0,      atmosphereMieScaleHeight: 3200.0,      atmosphereMieAnisotropy: 0.9,      hsbShift: new Cartesian3(),      center3D: undefined,      rtc: new Cartesian3(),      modifiedModelView: new Matrix4(),      tileRectangle: new Cartesian4(),      terrainExaggerationAndRelativeHeight: new Cartesian2(1.0, 0.0),      dayTextures: [],      dayTextureTranslationAndScale: [],      dayTextureTexCoordsRectangle: [],      dayTextureUseWebMercatorT: [],      dayTextureAlpha: [],      dayTextureNightAlpha: [],      dayTextureDayAlpha: [],      dayTextureBrightness: [],      dayTextureContrast: [],      dayTextureHue: [],      dayTextureSaturation: [],      dayTextureOneOverGamma: [],      dayTextureSplit: [],      dayTextureCutoutRectangles: [],      dayIntensity: 0.0,      colorsToAlpha: [],      southAndNorthLatitude: new Cartesian2(),      southMercatorYAndOneOverHeight: new Cartesian2(),      waterMask: undefined,      waterMaskTranslationAndScale: new Cartesian4(),      minMaxHeight: new Cartesian2(),      scaleAndBias: new Matrix4(),      clippingPlanesEdgeColor: Color.clone(Color.WHITE),      clippingPlanesEdgeWidth: 0.0,      localizedCartographicLimitRectangle: new Cartesian4(),      frontFaceAlphaByDistance: new Cartesian4(),      backFaceAlphaByDistance: new Cartesian4(),      localizedTranslucencyRectangle: new Cartesian4(),      undergroundColor: Color.clone(Color.TRANSPARENT),      undergroundColorAlphaByDistance: new Cartesian4(),      lambertDiffuseMultiplier: 0.0,    },  };  if (defined(globeSurfaceTileProvider.materialUniformMap)) {    return combine(uniformMap, globeSurfaceTileProvider.materialUniformMap);  }  return uniformMap;}function createWireframeVertexArrayIfNecessary(context, provider, tile) {  const surfaceTile = tile.data;  let mesh;  let vertexArray;  if (defined(surfaceTile.vertexArray)) {    mesh = surfaceTile.mesh;    vertexArray = surfaceTile.vertexArray;  } else if (    defined(surfaceTile.fill) &&    defined(surfaceTile.fill.vertexArray)  ) {    mesh = surfaceTile.fill.mesh;    vertexArray = surfaceTile.fill.vertexArray;  }  if (!defined(mesh) || !defined(vertexArray)) {    return;  }  if (defined(surfaceTile.wireframeVertexArray)) {    if (surfaceTile.wireframeVertexArray.mesh === mesh) {      return;    }    surfaceTile.wireframeVertexArray.destroy();    surfaceTile.wireframeVertexArray = undefined;  }  surfaceTile.wireframeVertexArray = createWireframeVertexArray(    context,    vertexArray,    mesh  );  surfaceTile.wireframeVertexArray.mesh = mesh;}/** * Creates a vertex array for wireframe rendering of a terrain tile. * * @private * * @param {Context} context The context in which to create the vertex array. * @param {VertexArray} vertexArray The existing, non-wireframe vertex array.  The new vertex array *                      will share vertex buffers with this existing one. * @param {TerrainMesh} terrainMesh The terrain mesh containing non-wireframe indices. * @returns {VertexArray} The vertex array for wireframe rendering. */function createWireframeVertexArray(context, vertexArray, terrainMesh) {  const indices = terrainMesh.indices;  const geometry = {    indices: indices,    primitiveType: PrimitiveType.TRIANGLES,  };  GeometryPipeline.toWireframe(geometry);  const wireframeIndices = geometry.indices;  const wireframeIndexBuffer = Buffer.createIndexBuffer({    context: context,    typedArray: wireframeIndices,    usage: BufferUsage.STATIC_DRAW,    indexDatatype: IndexDatatype.fromSizeInBytes(      wireframeIndices.BYTES_PER_ELEMENT    ),  });  return new VertexArray({    context: context,    attributes: vertexArray._attributes,    indexBuffer: wireframeIndexBuffer,  });}let getDebugOrientedBoundingBox;let getDebugBoundingSphere;let debugDestroyPrimitive;(function () {  const instanceOBB = new GeometryInstance({    geometry: BoxOutlineGeometry.fromDimensions({      dimensions: new Cartesian3(2.0, 2.0, 2.0),    }),  });  const instanceSphere = new GeometryInstance({    geometry: new SphereOutlineGeometry({ radius: 1.0 }),  });  let modelMatrix = new Matrix4();  let previousVolume;  let primitive;  function createDebugPrimitive(instance) {    return new Primitive({      geometryInstances: instance,      appearance: new PerInstanceColorAppearance({        translucent: false,        flat: true,      }),      asynchronous: false,    });  }  getDebugOrientedBoundingBox = function (obb, color) {    if (obb === previousVolume) {      return primitive;    }    debugDestroyPrimitive();    previousVolume = obb;    modelMatrix = Matrix4.fromRotationTranslation(      obb.halfAxes,      obb.center,      modelMatrix    );    instanceOBB.modelMatrix = modelMatrix;    instanceOBB.attributes.color = ColorGeometryInstanceAttribute.fromColor(      color    );    primitive = createDebugPrimitive(instanceOBB);    return primitive;  };  getDebugBoundingSphere = function (sphere, color) {    if (sphere === previousVolume) {      return primitive;    }    debugDestroyPrimitive();    previousVolume = sphere;    modelMatrix = Matrix4.fromTranslation(sphere.center, modelMatrix);    modelMatrix = Matrix4.multiplyByUniformScale(      modelMatrix,      sphere.radius,      modelMatrix    );    instanceSphere.modelMatrix = modelMatrix;    instanceSphere.attributes.color = ColorGeometryInstanceAttribute.fromColor(      color    );    primitive = createDebugPrimitive(instanceSphere);    return primitive;  };  debugDestroyPrimitive = function () {    if (defined(primitive)) {      primitive.destroy();      primitive = undefined;      previousVolume = undefined;    }  };})();const otherPassesInitialColor = new Cartesian4(0.0, 0.0, 0.0, 0.0);const surfaceShaderSetOptionsScratch = {  frameState: undefined,  surfaceTile: undefined,  numberOfDayTextures: undefined,  applyBrightness: undefined,  applyContrast: undefined,  applyHue: undefined,  applySaturation: undefined,  applyGamma: undefined,  applyAlpha: undefined,  applyDayNightAlpha: undefined,  applySplit: undefined,  showReflectiveOcean: undefined,  showOceanWaves: undefined,  enableLighting: undefined,  dynamicAtmosphereLighting: undefined,  dynamicAtmosphereLightingFromSun: undefined,  showGroundAtmosphere: undefined,  perFragmentGroundAtmosphere: undefined,  hasVertexNormals: undefined,  useWebMercatorProjection: undefined,  enableFog: undefined,  enableClippingPlanes: undefined,  clippingPlanes: undefined,  clippedByBoundaries: undefined,  hasImageryLayerCutout: undefined,  colorCorrect: undefined,  colorToAlpha: undefined,  hasGeodeticSurfaceNormals: undefined,  hasExaggeration: undefined,};const defaultUndergroundColor = Color.TRANSPARENT;const defaultUndergroundColorAlphaByDistance = new NearFarScalar();function addDrawCommandsForTile(tileProvider, tile, frameState) {  const surfaceTile = tile.data;  if (!defined(surfaceTile.vertexArray)) {    if (surfaceTile.fill === undefined) {      // No fill was created for this tile, probably because this tile is not connected to      // any renderable tiles. So create a simple tile in the middle of the tile's possible      // height range.      surfaceTile.fill = new TerrainFillMesh(tile);    }    surfaceTile.fill.update(tileProvider, frameState);  }  const creditDisplay = frameState.creditDisplay;  const terrainData = surfaceTile.terrainData;  if (defined(terrainData) && defined(terrainData.credits)) {    const tileCredits = terrainData.credits;    for (      let tileCreditIndex = 0, tileCreditLength = tileCredits.length;      tileCreditIndex < tileCreditLength;      ++tileCreditIndex    ) {      creditDisplay.addCredit(tileCredits[tileCreditIndex]);    }  }  let maxTextures = ContextLimits.maximumTextureImageUnits;  let waterMaskTexture = surfaceTile.waterMaskTexture;  let waterMaskTranslationAndScale = surfaceTile.waterMaskTranslationAndScale;  if (!defined(waterMaskTexture) && defined(surfaceTile.fill)) {    waterMaskTexture = surfaceTile.fill.waterMaskTexture;    waterMaskTranslationAndScale =      surfaceTile.fill.waterMaskTranslationAndScale;  }  const cameraUnderground = frameState.cameraUnderground;  const globeTranslucencyState = frameState.globeTranslucencyState;  const translucent = globeTranslucencyState.translucent;  const frontFaceAlphaByDistance =    globeTranslucencyState.frontFaceAlphaByDistance;  const backFaceAlphaByDistance =    globeTranslucencyState.backFaceAlphaByDistance;  const translucencyRectangle = globeTranslucencyState.rectangle;  const undergroundColor = defaultValue(    tileProvider.undergroundColor,    defaultUndergroundColor  );  const undergroundColorAlphaByDistance = defaultValue(    tileProvider.undergroundColorAlphaByDistance,    defaultUndergroundColorAlphaByDistance  );  const showUndergroundColor =    isUndergroundVisible(tileProvider, frameState) &&    frameState.mode === SceneMode.SCENE3D &&    undergroundColor.alpha > 0.0 &&    (undergroundColorAlphaByDistance.nearValue > 0.0 ||      undergroundColorAlphaByDistance.farValue > 0.0);  const lambertDiffuseMultiplier = tileProvider.lambertDiffuseMultiplier;  const showReflectiveOcean =    tileProvider.hasWaterMask && defined(waterMaskTexture);  const oceanNormalMap = tileProvider.oceanNormalMap;  const showOceanWaves = showReflectiveOcean && defined(oceanNormalMap);  const hasVertexNormals =    tileProvider.terrainProvider.ready &&    tileProvider.terrainProvider.hasVertexNormals;  const enableFog =    frameState.fog.enabled && frameState.fog.renderable && !cameraUnderground;  const showGroundAtmosphere =    tileProvider.showGroundAtmosphere && frameState.mode === SceneMode.SCENE3D;  const castShadows =    ShadowMode.castShadows(tileProvider.shadows) && !translucent;  const receiveShadows =    ShadowMode.receiveShadows(tileProvider.shadows) && !translucent;  const hueShift = tileProvider.hueShift;  const saturationShift = tileProvider.saturationShift;  const brightnessShift = tileProvider.brightnessShift;  let colorCorrect = !(    CesiumMath.equalsEpsilon(hueShift, 0.0, CesiumMath.EPSILON7) &&    CesiumMath.equalsEpsilon(saturationShift, 0.0, CesiumMath.EPSILON7) &&    CesiumMath.equalsEpsilon(brightnessShift, 0.0, CesiumMath.EPSILON7)  );  let perFragmentGroundAtmosphere = false;  if (showGroundAtmosphere) {    const cameraDistance = Cartesian3.magnitude(frameState.camera.positionWC);    const fadeOutDistance = tileProvider.nightFadeOutDistance;    perFragmentGroundAtmosphere = cameraDistance > fadeOutDistance;  }  if (showReflectiveOcean) {    --maxTextures;  }  if (showOceanWaves) {    --maxTextures;  }  if (    defined(frameState.shadowState) &&    frameState.shadowState.shadowsEnabled  ) {    --maxTextures;  }  if (    defined(tileProvider.clippingPlanes) &&    tileProvider.clippingPlanes.enabled  ) {    --maxTextures;  }  maxTextures -= globeTranslucencyState.numberOfTextureUniforms;  const mesh = surfaceTile.renderedMesh;  let rtc = mesh.center;  const encoding = mesh.encoding;  const tileBoundingRegion = surfaceTile.tileBoundingRegion;  const exaggeration = frameState.terrainExaggeration;  const exaggerationRelativeHeight =    frameState.terrainExaggerationRelativeHeight;  const hasExaggeration = exaggeration !== 1.0;  const hasGeodeticSurfaceNormals = encoding.hasGeodeticSurfaceNormals;  // Not used in 3D.  const tileRectangle = tileRectangleScratch;  // Only used for Mercator projections.  let southLatitude = 0.0;  let northLatitude = 0.0;  let southMercatorY = 0.0;  let oneOverMercatorHeight = 0.0;  let useWebMercatorProjection = false;  if (frameState.mode !== SceneMode.SCENE3D) {    const projection = frameState.mapProjection;    const southwest = projection.project(      Rectangle.southwest(tile.rectangle),      southwestScratch    );    const northeast = projection.project(      Rectangle.northeast(tile.rectangle),      northeastScratch    );    tileRectangle.x = southwest.x;    tileRectangle.y = southwest.y;    tileRectangle.z = northeast.x;    tileRectangle.w = northeast.y;    // In 2D and Columbus View, use the center of the tile for RTC rendering.    if (frameState.mode !== SceneMode.MORPHING) {      rtc = rtcScratch;      rtc.x = 0.0;      rtc.y = (tileRectangle.z + tileRectangle.x) * 0.5;      rtc.z = (tileRectangle.w + tileRectangle.y) * 0.5;      tileRectangle.x -= rtc.y;      tileRectangle.y -= rtc.z;      tileRectangle.z -= rtc.y;      tileRectangle.w -= rtc.z;    }    if (      frameState.mode === SceneMode.SCENE2D &&      encoding.quantization === TerrainQuantization.BITS12    ) {      // In 2D, the texture coordinates of the tile are interpolated over the rectangle to get the position in the vertex shader.      // When the texture coordinates are quantized, error is introduced. This can be seen through the 1px wide cracking      // between the quantized tiles in 2D. To compensate for the error, move the expand the rectangle in each direction by      // half the error amount.      const epsilon = (1.0 / (Math.pow(2.0, 12.0) - 1.0)) * 0.5;      const widthEpsilon = (tileRectangle.z - tileRectangle.x) * epsilon;      const heightEpsilon = (tileRectangle.w - tileRectangle.y) * epsilon;      tileRectangle.x -= widthEpsilon;      tileRectangle.y -= heightEpsilon;      tileRectangle.z += widthEpsilon;      tileRectangle.w += heightEpsilon;    }    if (projection instanceof WebMercatorProjection) {      southLatitude = tile.rectangle.south;      northLatitude = tile.rectangle.north;      southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(        southLatitude      );      oneOverMercatorHeight =        1.0 /        (WebMercatorProjection.geodeticLatitudeToMercatorAngle(northLatitude) -          southMercatorY);      useWebMercatorProjection = true;    }  }  const surfaceShaderSetOptions = surfaceShaderSetOptionsScratch;  surfaceShaderSetOptions.frameState = frameState;  surfaceShaderSetOptions.surfaceTile = surfaceTile;  surfaceShaderSetOptions.showReflectiveOcean = showReflectiveOcean;  surfaceShaderSetOptions.showOceanWaves = showOceanWaves;  surfaceShaderSetOptions.enableLighting = tileProvider.enableLighting;  surfaceShaderSetOptions.dynamicAtmosphereLighting =    tileProvider.dynamicAtmosphereLighting;  surfaceShaderSetOptions.dynamicAtmosphereLightingFromSun =    tileProvider.dynamicAtmosphereLightingFromSun;  surfaceShaderSetOptions.showGroundAtmosphere = showGroundAtmosphere;  surfaceShaderSetOptions.atmosphereLightIntensity =    tileProvider.atmosphereLightIntensity;  surfaceShaderSetOptions.atmosphereRayleighCoefficient =    tileProvider.atmosphereRayleighCoefficient;  surfaceShaderSetOptions.atmosphereMieCoefficient =    tileProvider.atmosphereMieCoefficient;  surfaceShaderSetOptions.atmosphereRayleighScaleHeight =    tileProvider.atmosphereRayleighScaleHeight;  surfaceShaderSetOptions.atmosphereMieScaleHeight =    tileProvider.atmosphereMieScaleHeight;  surfaceShaderSetOptions.atmosphereMieAnisotropy =    tileProvider.atmosphereMieAnisotropy;  surfaceShaderSetOptions.perFragmentGroundAtmosphere = perFragmentGroundAtmosphere;  surfaceShaderSetOptions.hasVertexNormals = hasVertexNormals;  surfaceShaderSetOptions.useWebMercatorProjection = useWebMercatorProjection;  surfaceShaderSetOptions.clippedByBoundaries = surfaceTile.clippedByBoundaries;  surfaceShaderSetOptions.hasGeodeticSurfaceNormals = hasGeodeticSurfaceNormals;  surfaceShaderSetOptions.hasExaggeration = hasExaggeration;  const tileImageryCollection = surfaceTile.imagery;  let imageryIndex = 0;  const imageryLen = tileImageryCollection.length;  const showSkirts =    tileProvider.showSkirts && !cameraUnderground && !translucent;  const backFaceCulling =    tileProvider.backFaceCulling && !cameraUnderground && !translucent;  const firstPassRenderState = backFaceCulling    ? tileProvider._renderState    : tileProvider._disableCullingRenderState;  const otherPassesRenderState = backFaceCulling    ? tileProvider._blendRenderState    : tileProvider._disableCullingBlendRenderState;  let renderState = firstPassRenderState;  let initialColor = tileProvider._firstPassInitialColor;  const context = frameState.context;  if (!defined(tileProvider._debug.boundingSphereTile)) {    debugDestroyPrimitive();  }  const materialUniformMapChanged =    tileProvider._materialUniformMap !== tileProvider.materialUniformMap;  if (materialUniformMapChanged) {    tileProvider._materialUniformMap = tileProvider.materialUniformMap;    const drawCommandsLength = tileProvider._drawCommands.length;    for (let i = 0; i < drawCommandsLength; ++i) {      tileProvider._uniformMaps[i] = createTileUniformMap(        frameState,        tileProvider      );    }  }  do {    let numberOfDayTextures = 0;    let command;    let uniformMap;    if (tileProvider._drawCommands.length <= tileProvider._usedDrawCommands) {      command = new DrawCommand();      command.owner = tile;      command.cull = false;      command.boundingVolume = new BoundingSphere();      command.orientedBoundingBox = undefined;      uniformMap = createTileUniformMap(frameState, tileProvider);      tileProvider._drawCommands.push(command);      tileProvider._uniformMaps.push(uniformMap);    } else {      command = tileProvider._drawCommands[tileProvider._usedDrawCommands];      uniformMap = tileProvider._uniformMaps[tileProvider._usedDrawCommands];    }    command.owner = tile;    ++tileProvider._usedDrawCommands;    if (tile === tileProvider._debug.boundingSphereTile) {      const obb = tileBoundingRegion.boundingVolume;      const boundingSphere = tileBoundingRegion.boundingSphere;      // If a debug primitive already exists for this tile, it will not be      // re-created, to avoid allocation every frame. If it were possible      // to have more than one selected tile, this would have to change.      if (defined(obb)) {        getDebugOrientedBoundingBox(obb, Color.RED).update(frameState);      } else if (defined(boundingSphere)) {        getDebugBoundingSphere(boundingSphere, Color.RED).update(frameState);      }    }    const uniformMapProperties = uniformMap.properties;    Cartesian4.clone(initialColor, uniformMapProperties.initialColor);    uniformMapProperties.oceanNormalMap = oceanNormalMap;    uniformMapProperties.lightingFadeDistance.x =      tileProvider.lightingFadeOutDistance;    uniformMapProperties.lightingFadeDistance.y =      tileProvider.lightingFadeInDistance;    uniformMapProperties.nightFadeDistance.x =      tileProvider.nightFadeOutDistance;    uniformMapProperties.nightFadeDistance.y = tileProvider.nightFadeInDistance;    uniformMapProperties.atmosphereLightIntensity =      tileProvider.atmosphereLightIntensity;    uniformMapProperties.atmosphereRayleighCoefficient =      tileProvider.atmosphereRayleighCoefficient;    uniformMapProperties.atmosphereMieCoefficient =      tileProvider.atmosphereMieCoefficient;    uniformMapProperties.atmosphereRayleighScaleHeight =      tileProvider.atmosphereRayleighScaleHeight;    uniformMapProperties.atmosphereMieScaleHeight =      tileProvider.atmosphereMieScaleHeight;    uniformMapProperties.atmosphereMieAnisotropy =      tileProvider.atmosphereMieAnisotropy;    uniformMapProperties.zoomedOutOceanSpecularIntensity =      tileProvider.zoomedOutOceanSpecularIntensity;    const frontFaceAlphaByDistanceFinal = cameraUnderground      ? backFaceAlphaByDistance      : frontFaceAlphaByDistance;    const backFaceAlphaByDistanceFinal = cameraUnderground      ? frontFaceAlphaByDistance      : backFaceAlphaByDistance;    if (defined(frontFaceAlphaByDistanceFinal)) {      Cartesian4.fromElements(        frontFaceAlphaByDistanceFinal.near,        frontFaceAlphaByDistanceFinal.nearValue,        frontFaceAlphaByDistanceFinal.far,        frontFaceAlphaByDistanceFinal.farValue,        uniformMapProperties.frontFaceAlphaByDistance      );      Cartesian4.fromElements(        backFaceAlphaByDistanceFinal.near,        backFaceAlphaByDistanceFinal.nearValue,        backFaceAlphaByDistanceFinal.far,        backFaceAlphaByDistanceFinal.farValue,        uniformMapProperties.backFaceAlphaByDistance      );    }    Cartesian4.fromElements(      undergroundColorAlphaByDistance.near,      undergroundColorAlphaByDistance.nearValue,      undergroundColorAlphaByDistance.far,      undergroundColorAlphaByDistance.farValue,      uniformMapProperties.undergroundColorAlphaByDistance    );    Color.clone(undergroundColor, uniformMapProperties.undergroundColor);    uniformMapProperties.lambertDiffuseMultiplier = lambertDiffuseMultiplier;    const highlightFillTile =      !defined(surfaceTile.vertexArray) &&      defined(tileProvider.fillHighlightColor) &&      tileProvider.fillHighlightColor.alpha > 0.0;    if (highlightFillTile) {      Color.clone(        tileProvider.fillHighlightColor,        uniformMapProperties.fillHighlightColor      );    }    uniformMapProperties.terrainExaggerationAndRelativeHeight.x = exaggeration;    uniformMapProperties.terrainExaggerationAndRelativeHeight.y = exaggerationRelativeHeight;    uniformMapProperties.center3D = mesh.center;    Cartesian3.clone(rtc, uniformMapProperties.rtc);    Cartesian4.clone(tileRectangle, uniformMapProperties.tileRectangle);    uniformMapProperties.southAndNorthLatitude.x = southLatitude;    uniformMapProperties.southAndNorthLatitude.y = northLatitude;    uniformMapProperties.southMercatorYAndOneOverHeight.x = southMercatorY;    uniformMapProperties.southMercatorYAndOneOverHeight.y = oneOverMercatorHeight;    // Convert tile limiter rectangle from cartographic to texture space using the tileRectangle.    const localizedCartographicLimitRectangle = localizedCartographicLimitRectangleScratch;    const cartographicLimitRectangle = clipRectangleAntimeridian(      tile.rectangle,      tileProvider.cartographicLimitRectangle    );    const localizedTranslucencyRectangle = localizedTranslucencyRectangleScratch;    const clippedTranslucencyRectangle = clipRectangleAntimeridian(      tile.rectangle,      translucencyRectangle    );    Cartesian3.fromElements(      hueShift,      saturationShift,      brightnessShift,      uniformMapProperties.hsbShift    );    const cartographicTileRectangle = tile.rectangle;    const inverseTileWidth = 1.0 / cartographicTileRectangle.width;    const inverseTileHeight = 1.0 / cartographicTileRectangle.height;    localizedCartographicLimitRectangle.x =      (cartographicLimitRectangle.west - cartographicTileRectangle.west) *      inverseTileWidth;    localizedCartographicLimitRectangle.y =      (cartographicLimitRectangle.south - cartographicTileRectangle.south) *      inverseTileHeight;    localizedCartographicLimitRectangle.z =      (cartographicLimitRectangle.east - cartographicTileRectangle.west) *      inverseTileWidth;    localizedCartographicLimitRectangle.w =      (cartographicLimitRectangle.north - cartographicTileRectangle.south) *      inverseTileHeight;    Cartesian4.clone(      localizedCartographicLimitRectangle,      uniformMapProperties.localizedCartographicLimitRectangle    );    localizedTranslucencyRectangle.x =      (clippedTranslucencyRectangle.west - cartographicTileRectangle.west) *      inverseTileWidth;    localizedTranslucencyRectangle.y =      (clippedTranslucencyRectangle.south - cartographicTileRectangle.south) *      inverseTileHeight;    localizedTranslucencyRectangle.z =      (clippedTranslucencyRectangle.east - cartographicTileRectangle.west) *      inverseTileWidth;    localizedTranslucencyRectangle.w =      (clippedTranslucencyRectangle.north - cartographicTileRectangle.south) *      inverseTileHeight;    Cartesian4.clone(      localizedTranslucencyRectangle,      uniformMapProperties.localizedTranslucencyRectangle    );    // For performance, use fog in the shader only when the tile is in fog.    const applyFog =      enableFog &&      CesiumMath.fog(tile._distance, frameState.fog.density) >        CesiumMath.EPSILON3;    colorCorrect = colorCorrect && (applyFog || showGroundAtmosphere);    let applyBrightness = false;    let applyContrast = false;    let applyHue = false;    let applySaturation = false;    let applyGamma = false;    let applyAlpha = false;    let applyDayNightAlpha = false;    let applySplit = false;    let applyCutout = false;    let applyColorToAlpha = false;    while (numberOfDayTextures < maxTextures && imageryIndex < imageryLen) {      const tileImagery = tileImageryCollection[imageryIndex];      const imagery = tileImagery.readyImagery;      ++imageryIndex;      if (!defined(imagery) || imagery.imageryLayer.alpha === 0.0) {        continue;      }      const texture = tileImagery.useWebMercatorT        ? imagery.textureWebMercator        : imagery.texture;      //>>includeStart('debug', pragmas.debug);      if (!defined(texture)) {        // Our "ready" texture isn't actually ready.  This should never happen.        //        // Side note: It IS possible for it to not be in the READY ImageryState, though.        // This can happen when a single imagery tile is shared by two terrain tiles (common)        // and one of them (A) needs a geographic version of the tile because it is near the poles,        // and the other (B) does not.  B can and will transition the imagery tile to the READY state        // without reprojecting to geographic.  Then, later, A will deem that same tile not-ready-yet        // because it only has the Web Mercator texture, and flip it back to the TRANSITIONING state.        // The imagery tile won't be in the READY state anymore, but it's still READY enough for B's        // purposes.        throw new DeveloperError("readyImagery is not actually ready!");      }      //>>includeEnd('debug');      const imageryLayer = imagery.imageryLayer;      if (!defined(tileImagery.textureTranslationAndScale)) {        tileImagery.textureTranslationAndScale = imageryLayer._calculateTextureTranslationAndScale(          tile,          tileImagery        );      }      uniformMapProperties.dayTextures[numberOfDayTextures] = texture;      uniformMapProperties.dayTextureTranslationAndScale[numberOfDayTextures] =        tileImagery.textureTranslationAndScale;      uniformMapProperties.dayTextureTexCoordsRectangle[numberOfDayTextures] =        tileImagery.textureCoordinateRectangle;      uniformMapProperties.dayTextureUseWebMercatorT[numberOfDayTextures] =        tileImagery.useWebMercatorT;      uniformMapProperties.dayTextureAlpha[numberOfDayTextures] =        imageryLayer.alpha;      applyAlpha =        applyAlpha ||        uniformMapProperties.dayTextureAlpha[numberOfDayTextures] !== 1.0;      uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] =        imageryLayer.nightAlpha;      applyDayNightAlpha =        applyDayNightAlpha ||        uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] !== 1.0;      uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] =        imageryLayer.dayAlpha;      applyDayNightAlpha =        applyDayNightAlpha ||        uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] !== 1.0;      uniformMapProperties.dayTextureBrightness[numberOfDayTextures] =        imageryLayer.brightness;      applyBrightness =        applyBrightness ||        uniformMapProperties.dayTextureBrightness[numberOfDayTextures] !==          ImageryLayer.DEFAULT_BRIGHTNESS;      uniformMapProperties.dayTextureContrast[numberOfDayTextures] =        imageryLayer.contrast;      applyContrast =        applyContrast ||        uniformMapProperties.dayTextureContrast[numberOfDayTextures] !==          ImageryLayer.DEFAULT_CONTRAST;      uniformMapProperties.dayTextureHue[numberOfDayTextures] =        imageryLayer.hue;      applyHue =        applyHue ||        uniformMapProperties.dayTextureHue[numberOfDayTextures] !==          ImageryLayer.DEFAULT_HUE;      uniformMapProperties.dayTextureSaturation[numberOfDayTextures] =        imageryLayer.saturation;      applySaturation =        applySaturation ||        uniformMapProperties.dayTextureSaturation[numberOfDayTextures] !==          ImageryLayer.DEFAULT_SATURATION;      uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] =        1.0 / imageryLayer.gamma;      applyGamma =        applyGamma ||        uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] !==          1.0 / ImageryLayer.DEFAULT_GAMMA;      uniformMapProperties.dayTextureSplit[numberOfDayTextures] =        imageryLayer.splitDirection;      applySplit =        applySplit ||        uniformMapProperties.dayTextureSplit[numberOfDayTextures] !== 0.0;      // Update cutout rectangle      let dayTextureCutoutRectangle =        uniformMapProperties.dayTextureCutoutRectangles[numberOfDayTextures];      if (!defined(dayTextureCutoutRectangle)) {        dayTextureCutoutRectangle = uniformMapProperties.dayTextureCutoutRectangles[          numberOfDayTextures        ] = new Cartesian4();      }      Cartesian4.clone(Cartesian4.ZERO, dayTextureCutoutRectangle);      if (defined(imageryLayer.cutoutRectangle)) {        const cutoutRectangle = clipRectangleAntimeridian(          cartographicTileRectangle,          imageryLayer.cutoutRectangle        );        const intersection = Rectangle.simpleIntersection(          cutoutRectangle,          cartographicTileRectangle,          rectangleIntersectionScratch        );        applyCutout = defined(intersection) || applyCutout;        dayTextureCutoutRectangle.x =          (cutoutRectangle.west - cartographicTileRectangle.west) *          inverseTileWidth;        dayTextureCutoutRectangle.y =          (cutoutRectangle.south - cartographicTileRectangle.south) *          inverseTileHeight;        dayTextureCutoutRectangle.z =          (cutoutRectangle.east - cartographicTileRectangle.west) *          inverseTileWidth;        dayTextureCutoutRectangle.w =          (cutoutRectangle.north - cartographicTileRectangle.south) *          inverseTileHeight;      }      // Update color to alpha      let colorToAlpha =        uniformMapProperties.colorsToAlpha[numberOfDayTextures];      if (!defined(colorToAlpha)) {        colorToAlpha = uniformMapProperties.colorsToAlpha[          numberOfDayTextures        ] = new Cartesian4();      }      const hasColorToAlpha =        defined(imageryLayer.colorToAlpha) &&        imageryLayer.colorToAlphaThreshold > 0.0;      applyColorToAlpha = applyColorToAlpha || hasColorToAlpha;      if (hasColorToAlpha) {        const color = imageryLayer.colorToAlpha;        colorToAlpha.x = color.red;        colorToAlpha.y = color.green;        colorToAlpha.z = color.blue;        colorToAlpha.w = imageryLayer.colorToAlphaThreshold;      } else {        colorToAlpha.w = -1.0;      }      if (defined(imagery.credits)) {        const credits = imagery.credits;        for (          let creditIndex = 0, creditLength = credits.length;          creditIndex < creditLength;          ++creditIndex        ) {          creditDisplay.addCredit(credits[creditIndex]);        }      }      ++numberOfDayTextures;    }    // trim texture array to the used length so we don't end up using old textures    // which might get destroyed eventually    uniformMapProperties.dayTextures.length = numberOfDayTextures;    uniformMapProperties.waterMask = waterMaskTexture;    Cartesian4.clone(      waterMaskTranslationAndScale,      uniformMapProperties.waterMaskTranslationAndScale    );    uniformMapProperties.minMaxHeight.x = encoding.minimumHeight;    uniformMapProperties.minMaxHeight.y = encoding.maximumHeight;    Matrix4.clone(encoding.matrix, uniformMapProperties.scaleAndBias);    // update clipping planes    const clippingPlanes = tileProvider._clippingPlanes;    const clippingPlanesEnabled =      defined(clippingPlanes) && clippingPlanes.enabled && tile.isClipped;    if (clippingPlanesEnabled) {      uniformMapProperties.clippingPlanesEdgeColor = Color.clone(        clippingPlanes.edgeColor,        uniformMapProperties.clippingPlanesEdgeColor      );      uniformMapProperties.clippingPlanesEdgeWidth = clippingPlanes.edgeWidth;    }    surfaceShaderSetOptions.numberOfDayTextures = numberOfDayTextures;    surfaceShaderSetOptions.applyBrightness = applyBrightness;    surfaceShaderSetOptions.applyContrast = applyContrast;    surfaceShaderSetOptions.applyHue = applyHue;    surfaceShaderSetOptions.applySaturation = applySaturation;    surfaceShaderSetOptions.applyGamma = applyGamma;    surfaceShaderSetOptions.applyAlpha = applyAlpha;    surfaceShaderSetOptions.applyDayNightAlpha = applyDayNightAlpha;    surfaceShaderSetOptions.applySplit = applySplit;    surfaceShaderSetOptions.enableFog = applyFog;    surfaceShaderSetOptions.enableClippingPlanes = clippingPlanesEnabled;    surfaceShaderSetOptions.clippingPlanes = clippingPlanes;    surfaceShaderSetOptions.hasImageryLayerCutout = applyCutout;    surfaceShaderSetOptions.colorCorrect = colorCorrect;    surfaceShaderSetOptions.highlightFillTile = highlightFillTile;    surfaceShaderSetOptions.colorToAlpha = applyColorToAlpha;    surfaceShaderSetOptions.showUndergroundColor = showUndergroundColor;    surfaceShaderSetOptions.translucent = translucent;    let count = surfaceTile.renderedMesh.indices.length;    if (!showSkirts) {      count = surfaceTile.renderedMesh.indexCountWithoutSkirts;    }    command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(      surfaceShaderSetOptions    );    command.castShadows = castShadows;    command.receiveShadows = receiveShadows;    command.renderState = renderState;    command.primitiveType = PrimitiveType.TRIANGLES;    command.vertexArray =      surfaceTile.vertexArray || surfaceTile.fill.vertexArray;    command.count = count;    command.uniformMap = uniformMap;    command.pass = Pass.GLOBE;    if (tileProvider._debug.wireframe) {      createWireframeVertexArrayIfNecessary(context, tileProvider, tile);      if (defined(surfaceTile.wireframeVertexArray)) {        command.vertexArray = surfaceTile.wireframeVertexArray;        command.primitiveType = PrimitiveType.LINES;        command.count = count * 2;      }    }    let boundingVolume = command.boundingVolume;    const orientedBoundingBox = command.orientedBoundingBox;    if (frameState.mode !== SceneMode.SCENE3D) {      BoundingSphere.fromRectangleWithHeights2D(        tile.rectangle,        frameState.mapProjection,        tileBoundingRegion.minimumHeight,        tileBoundingRegion.maximumHeight,        boundingVolume      );      Cartesian3.fromElements(        boundingVolume.center.z,        boundingVolume.center.x,        boundingVolume.center.y,        boundingVolume.center      );      if (frameState.mode === SceneMode.MORPHING) {        boundingVolume = BoundingSphere.union(          tileBoundingRegion.boundingSphere,          boundingVolume,          boundingVolume        );      }    } else {      command.boundingVolume = BoundingSphere.clone(        tileBoundingRegion.boundingSphere,        boundingVolume      );      command.orientedBoundingBox = OrientedBoundingBox.clone(        tileBoundingRegion.boundingVolume,        orientedBoundingBox      );    }    command.dirty = true;    if (translucent) {      globeTranslucencyState.updateDerivedCommands(command, frameState);    }    pushCommand(command, frameState);    renderState = otherPassesRenderState;    initialColor = otherPassesInitialColor;  } while (imageryIndex < imageryLen);}export default GlobeSurfaceTileProvider;
 |