GlobeSurfaceTile.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import Cartographic from "../Core/Cartographic.js";
  5. import defined from "../Core/defined.js";
  6. import IndexDatatype from "../Core/IndexDatatype.js";
  7. import IntersectionTests from "../Core/IntersectionTests.js";
  8. import PixelFormat from "../Core/PixelFormat.js";
  9. import Ray from "../Core/Ray.js";
  10. import Request from "../Core/Request.js";
  11. import RequestState from "../Core/RequestState.js";
  12. import RequestType from "../Core/RequestType.js";
  13. import TerrainEncoding from "../Core/TerrainEncoding.js";
  14. import TileProviderError from "../Core/TileProviderError.js";
  15. import Buffer from "../Renderer/Buffer.js";
  16. import BufferUsage from "../Renderer/BufferUsage.js";
  17. import PixelDatatype from "../Renderer/PixelDatatype.js";
  18. import Sampler from "../Renderer/Sampler.js";
  19. import Texture from "../Renderer/Texture.js";
  20. import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
  21. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  22. import TextureWrap from "../Renderer/TextureWrap.js";
  23. import VertexArray from "../Renderer/VertexArray.js";
  24. import ImageryState from "./ImageryState.js";
  25. import QuadtreeTileLoadState from "./QuadtreeTileLoadState.js";
  26. import SceneMode from "./SceneMode.js";
  27. import TerrainState from "./TerrainState.js";
  28. /**
  29. * Contains additional information about a {@link QuadtreeTile} of the globe's surface, and
  30. * encapsulates state transition logic for loading tiles.
  31. *
  32. * @constructor
  33. * @alias GlobeSurfaceTile
  34. * @private
  35. */
  36. function GlobeSurfaceTile() {
  37. /**
  38. * The {@link TileImagery} attached to this tile.
  39. * @type {TileImagery[]}
  40. * @default []
  41. */
  42. this.imagery = [];
  43. this.waterMaskTexture = undefined;
  44. this.waterMaskTranslationAndScale = new Cartesian4(0.0, 0.0, 1.0, 1.0);
  45. this.terrainData = undefined;
  46. this.vertexArray = undefined;
  47. /**
  48. * A bounding region used to estimate distance to the tile. The horizontal bounds are always tight-fitting,
  49. * but the `minimumHeight` and `maximumHeight` properties may be derived from the min/max of an ancestor tile
  50. * and be quite loose-fitting and thus very poor for estimating distance.
  51. * @type {TileBoundingRegion}
  52. */
  53. this.tileBoundingRegion = undefined;
  54. this.occludeePointInScaledSpace = new Cartesian3();
  55. this.boundingVolumeSourceTile = undefined;
  56. this.boundingVolumeIsFromMesh = false;
  57. this.terrainState = TerrainState.UNLOADED;
  58. this.mesh = undefined;
  59. this.fill = undefined;
  60. this.pickBoundingSphere = new BoundingSphere();
  61. this.surfaceShader = undefined;
  62. this.isClipped = true;
  63. this.clippedByBoundaries = false;
  64. }
  65. Object.defineProperties(GlobeSurfaceTile.prototype, {
  66. /**
  67. * Gets a value indicating whether or not this tile is eligible to be unloaded.
  68. * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
  69. * such as a request for data, is in progress on it. A tile will never be
  70. * unloaded while it is needed for rendering, regardless of the value of this
  71. * property.
  72. * @memberof GlobeSurfaceTile.prototype
  73. * @type {boolean}
  74. */
  75. eligibleForUnloading: {
  76. get: function () {
  77. // Do not remove tiles that are transitioning or that have
  78. // imagery that is transitioning.
  79. const terrainState = this.terrainState;
  80. const loadingIsTransitioning =
  81. terrainState === TerrainState.RECEIVING ||
  82. terrainState === TerrainState.TRANSFORMING;
  83. let shouldRemoveTile = !loadingIsTransitioning;
  84. const imagery = this.imagery;
  85. for (let i = 0, len = imagery.length; shouldRemoveTile && i < len; ++i) {
  86. const tileImagery = imagery[i];
  87. shouldRemoveTile =
  88. !defined(tileImagery.loadingImagery) ||
  89. tileImagery.loadingImagery.state !== ImageryState.TRANSITIONING;
  90. }
  91. return shouldRemoveTile;
  92. },
  93. },
  94. /**
  95. * Gets the {@link TerrainMesh} that is used for rendering this tile, if any.
  96. * Returns the value of the {@link GlobeSurfaceTile#mesh} property if
  97. * {@link GlobeSurfaceTile#vertexArray} is defined. Otherwise, It returns the
  98. * {@link TerrainFillMesh#mesh} property of the {@link GlobeSurfaceTile#fill}.
  99. * If there is no fill, it returns undefined.
  100. *
  101. * @memberof GlobeSurfaceTile.prototype
  102. * @type {TerrainMesh}
  103. */
  104. renderedMesh: {
  105. get: function () {
  106. if (defined(this.vertexArray)) {
  107. return this.mesh;
  108. } else if (defined(this.fill)) {
  109. return this.fill.mesh;
  110. }
  111. return undefined;
  112. },
  113. },
  114. });
  115. const scratchCartographic = new Cartographic();
  116. function getPosition(encoding, mode, projection, vertices, index, result) {
  117. let position = encoding.getExaggeratedPosition(vertices, index, result);
  118. if (defined(mode) && mode !== SceneMode.SCENE3D) {
  119. const ellipsoid = projection.ellipsoid;
  120. const positionCartographic = ellipsoid.cartesianToCartographic(
  121. position,
  122. scratchCartographic
  123. );
  124. position = projection.project(positionCartographic, result);
  125. position = Cartesian3.fromElements(
  126. position.z,
  127. position.x,
  128. position.y,
  129. result
  130. );
  131. }
  132. return position;
  133. }
  134. const scratchV0 = new Cartesian3();
  135. const scratchV1 = new Cartesian3();
  136. const scratchV2 = new Cartesian3();
  137. GlobeSurfaceTile.prototype.pick = function (
  138. ray,
  139. mode,
  140. projection,
  141. cullBackFaces,
  142. result
  143. ) {
  144. const mesh = this.renderedMesh;
  145. if (!defined(mesh)) {
  146. return undefined;
  147. }
  148. const vertices = mesh.vertices;
  149. const indices = mesh.indices;
  150. const encoding = mesh.encoding;
  151. const indicesLength = indices.length;
  152. let minT = Number.MAX_VALUE;
  153. for (let i = 0; i < indicesLength; i += 3) {
  154. const i0 = indices[i];
  155. const i1 = indices[i + 1];
  156. const i2 = indices[i + 2];
  157. const v0 = getPosition(encoding, mode, projection, vertices, i0, scratchV0);
  158. const v1 = getPosition(encoding, mode, projection, vertices, i1, scratchV1);
  159. const v2 = getPosition(encoding, mode, projection, vertices, i2, scratchV2);
  160. const t = IntersectionTests.rayTriangleParametric(
  161. ray,
  162. v0,
  163. v1,
  164. v2,
  165. cullBackFaces
  166. );
  167. if (defined(t) && t < minT && t >= 0.0) {
  168. minT = t;
  169. }
  170. }
  171. return minT !== Number.MAX_VALUE
  172. ? Ray.getPoint(ray, minT, result)
  173. : undefined;
  174. };
  175. GlobeSurfaceTile.prototype.freeResources = function () {
  176. if (defined(this.waterMaskTexture)) {
  177. --this.waterMaskTexture.referenceCount;
  178. if (this.waterMaskTexture.referenceCount === 0) {
  179. this.waterMaskTexture.destroy();
  180. }
  181. this.waterMaskTexture = undefined;
  182. }
  183. this.terrainData = undefined;
  184. this.terrainState = TerrainState.UNLOADED;
  185. this.mesh = undefined;
  186. this.fill = this.fill && this.fill.destroy();
  187. const imageryList = this.imagery;
  188. for (let i = 0, len = imageryList.length; i < len; ++i) {
  189. imageryList[i].freeResources();
  190. }
  191. this.imagery.length = 0;
  192. this.freeVertexArray();
  193. };
  194. GlobeSurfaceTile.prototype.freeVertexArray = function () {
  195. GlobeSurfaceTile._freeVertexArray(this.vertexArray);
  196. this.vertexArray = undefined;
  197. GlobeSurfaceTile._freeVertexArray(this.wireframeVertexArray);
  198. this.wireframeVertexArray = undefined;
  199. };
  200. GlobeSurfaceTile.initialize = function (
  201. tile,
  202. terrainProvider,
  203. imageryLayerCollection
  204. ) {
  205. let surfaceTile = tile.data;
  206. if (!defined(surfaceTile)) {
  207. surfaceTile = tile.data = new GlobeSurfaceTile();
  208. }
  209. if (tile.state === QuadtreeTileLoadState.START) {
  210. prepareNewTile(tile, terrainProvider, imageryLayerCollection);
  211. tile.state = QuadtreeTileLoadState.LOADING;
  212. }
  213. };
  214. GlobeSurfaceTile.processStateMachine = function (
  215. tile,
  216. frameState,
  217. terrainProvider,
  218. imageryLayerCollection,
  219. quadtree,
  220. vertexArraysToDestroy,
  221. terrainOnly
  222. ) {
  223. GlobeSurfaceTile.initialize(tile, terrainProvider, imageryLayerCollection);
  224. const surfaceTile = tile.data;
  225. if (tile.state === QuadtreeTileLoadState.LOADING) {
  226. processTerrainStateMachine(
  227. tile,
  228. frameState,
  229. terrainProvider,
  230. imageryLayerCollection,
  231. quadtree,
  232. vertexArraysToDestroy
  233. );
  234. }
  235. // From here down we're loading imagery, not terrain. We don't want to load imagery until
  236. // we're certain that the terrain tiles are actually visible, though. We'll load terrainOnly
  237. // in these scenarios:
  238. // * our bounding volume isn't accurate so we're not certain this tile is really visible (see GlobeSurfaceTileProvider#loadTile).
  239. // * we want to upsample from this tile but don't plan to render it (see processTerrainStateMachine).
  240. if (terrainOnly) {
  241. return;
  242. }
  243. const wasAlreadyRenderable = tile.renderable;
  244. // The terrain is renderable as soon as we have a valid vertex array.
  245. tile.renderable = defined(surfaceTile.vertexArray);
  246. // But it's not done loading until it's in the READY state.
  247. const isTerrainDoneLoading = surfaceTile.terrainState === TerrainState.READY;
  248. // If this tile's terrain and imagery are just upsampled from its parent, mark the tile as
  249. // upsampled only. We won't refine a tile if its four children are upsampled only.
  250. tile.upsampledFromParent =
  251. defined(surfaceTile.terrainData) &&
  252. surfaceTile.terrainData.wasCreatedByUpsampling();
  253. const isImageryDoneLoading = surfaceTile.processImagery(
  254. tile,
  255. terrainProvider,
  256. frameState
  257. );
  258. if (isTerrainDoneLoading && isImageryDoneLoading) {
  259. const callbacks = tile._loadedCallbacks;
  260. const newCallbacks = {};
  261. for (const layerId in callbacks) {
  262. if (callbacks.hasOwnProperty(layerId)) {
  263. if (!callbacks[layerId](tile)) {
  264. newCallbacks[layerId] = callbacks[layerId];
  265. }
  266. }
  267. }
  268. tile._loadedCallbacks = newCallbacks;
  269. tile.state = QuadtreeTileLoadState.DONE;
  270. }
  271. // Once a tile is renderable, it stays renderable, because doing otherwise would
  272. // cause detail (or maybe even the entire globe) to vanish when adding a new
  273. // imagery layer. `GlobeSurfaceTileProvider._onLayerAdded` sets renderable to
  274. // false for all affected tiles that are not currently being rendered.
  275. if (wasAlreadyRenderable) {
  276. tile.renderable = true;
  277. }
  278. };
  279. GlobeSurfaceTile.prototype.processImagery = function (
  280. tile,
  281. terrainProvider,
  282. frameState,
  283. skipLoading
  284. ) {
  285. const surfaceTile = tile.data;
  286. let isUpsampledOnly = tile.upsampledFromParent;
  287. let isAnyTileLoaded = false;
  288. let isDoneLoading = true;
  289. // Transition imagery states
  290. const tileImageryCollection = surfaceTile.imagery;
  291. let i, len;
  292. for (i = 0, len = tileImageryCollection.length; i < len; ++i) {
  293. const tileImagery = tileImageryCollection[i];
  294. if (!defined(tileImagery.loadingImagery)) {
  295. isUpsampledOnly = false;
  296. continue;
  297. }
  298. if (tileImagery.loadingImagery.state === ImageryState.PLACEHOLDER) {
  299. const imageryLayer = tileImagery.loadingImagery.imageryLayer;
  300. // ImageryProvider.ready is deprecated. This is here for backwards compatibility
  301. if (imageryLayer.ready && imageryLayer.imageryProvider._ready) {
  302. // Remove the placeholder and add the actual skeletons (if any)
  303. // at the same position. Then continue the loop at the same index.
  304. tileImagery.freeResources();
  305. tileImageryCollection.splice(i, 1);
  306. imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i);
  307. --i;
  308. len = tileImageryCollection.length;
  309. continue;
  310. } else {
  311. isUpsampledOnly = false;
  312. }
  313. }
  314. const thisTileDoneLoading = tileImagery.processStateMachine(
  315. tile,
  316. frameState,
  317. skipLoading
  318. );
  319. isDoneLoading = isDoneLoading && thisTileDoneLoading;
  320. // The imagery is renderable as soon as we have any renderable imagery for this region.
  321. isAnyTileLoaded =
  322. isAnyTileLoaded ||
  323. thisTileDoneLoading ||
  324. defined(tileImagery.readyImagery);
  325. isUpsampledOnly =
  326. isUpsampledOnly &&
  327. defined(tileImagery.loadingImagery) &&
  328. (tileImagery.loadingImagery.state === ImageryState.FAILED ||
  329. tileImagery.loadingImagery.state === ImageryState.INVALID);
  330. }
  331. tile.upsampledFromParent = isUpsampledOnly;
  332. // Allow rendering if any available layers are loaded
  333. tile.renderable = tile.renderable && (isAnyTileLoaded || isDoneLoading);
  334. return isDoneLoading;
  335. };
  336. function toggleGeodeticSurfaceNormals(
  337. surfaceTile,
  338. enabled,
  339. ellipsoid,
  340. frameState
  341. ) {
  342. const renderedMesh = surfaceTile.renderedMesh;
  343. const vertexBuffer = renderedMesh.vertices;
  344. const encoding = renderedMesh.encoding;
  345. const vertexCount = vertexBuffer.length / encoding.stride;
  346. // Calculate the new stride and generate a new buffer
  347. // Clone the other encoding, toggle geodetic surface normals, then clone again to get updated stride
  348. let newEncoding = TerrainEncoding.clone(encoding);
  349. newEncoding.hasGeodeticSurfaceNormals = enabled;
  350. newEncoding = TerrainEncoding.clone(newEncoding);
  351. const newStride = newEncoding.stride;
  352. const newVertexBuffer = new Float32Array(vertexCount * newStride);
  353. if (enabled) {
  354. encoding.addGeodeticSurfaceNormals(
  355. vertexBuffer,
  356. newVertexBuffer,
  357. ellipsoid
  358. );
  359. } else {
  360. encoding.removeGeodeticSurfaceNormals(vertexBuffer, newVertexBuffer);
  361. }
  362. renderedMesh.vertices = newVertexBuffer;
  363. renderedMesh.stride = newStride;
  364. // delete the old vertex array (which deletes the vertex buffer attached to it), and create a new vertex array with the new vertex buffer
  365. const isFill = renderedMesh !== surfaceTile.mesh;
  366. if (isFill) {
  367. GlobeSurfaceTile._freeVertexArray(surfaceTile.fill.vertexArray);
  368. surfaceTile.fill.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  369. frameState.context,
  370. renderedMesh
  371. );
  372. } else {
  373. GlobeSurfaceTile._freeVertexArray(surfaceTile.vertexArray);
  374. surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  375. frameState.context,
  376. renderedMesh
  377. );
  378. }
  379. GlobeSurfaceTile._freeVertexArray(surfaceTile.wireframeVertexArray);
  380. surfaceTile.wireframeVertexArray = undefined;
  381. }
  382. GlobeSurfaceTile.prototype.addGeodeticSurfaceNormals = function (
  383. ellipsoid,
  384. frameState
  385. ) {
  386. toggleGeodeticSurfaceNormals(this, true, ellipsoid, frameState);
  387. };
  388. GlobeSurfaceTile.prototype.removeGeodeticSurfaceNormals = function (
  389. frameState
  390. ) {
  391. toggleGeodeticSurfaceNormals(this, false, undefined, frameState);
  392. };
  393. GlobeSurfaceTile.prototype.updateExaggeration = function (
  394. tile,
  395. frameState,
  396. quadtree
  397. ) {
  398. const surfaceTile = this;
  399. const mesh = surfaceTile.renderedMesh;
  400. if (mesh === undefined) {
  401. return;
  402. }
  403. // Check the tile's terrain encoding to see if it has been exaggerated yet
  404. const exaggeration = frameState.terrainExaggeration;
  405. const exaggerationRelativeHeight =
  406. frameState.terrainExaggerationRelativeHeight;
  407. const hasExaggerationScale = exaggeration !== 1.0;
  408. const encoding = mesh.encoding;
  409. const encodingExaggerationScaleChanged =
  410. encoding.exaggeration !== exaggeration;
  411. const encodingRelativeHeightChanged =
  412. encoding.exaggerationRelativeHeight !== exaggerationRelativeHeight;
  413. if (encodingExaggerationScaleChanged || encodingRelativeHeightChanged) {
  414. // Turning exaggeration scale on/off requires adding or removing geodetic surface normals
  415. // Relative height only translates, so it has no effect on normals
  416. if (encodingExaggerationScaleChanged) {
  417. if (hasExaggerationScale && !encoding.hasGeodeticSurfaceNormals) {
  418. const ellipsoid = tile.tilingScheme.ellipsoid;
  419. surfaceTile.addGeodeticSurfaceNormals(ellipsoid, frameState);
  420. } else if (!hasExaggerationScale && encoding.hasGeodeticSurfaceNormals) {
  421. surfaceTile.removeGeodeticSurfaceNormals(frameState);
  422. }
  423. }
  424. encoding.exaggeration = exaggeration;
  425. encoding.exaggerationRelativeHeight = exaggerationRelativeHeight;
  426. // Notify the quadtree that this tile's height has changed
  427. if (quadtree !== undefined) {
  428. quadtree._tileToUpdateHeights.push(tile);
  429. const customData = tile.customData;
  430. const customDataLength = customData.length;
  431. for (let i = 0; i < customDataLength; i++) {
  432. // Restart the level so that a height update is triggered
  433. const data = customData[i];
  434. data.level = -1;
  435. }
  436. }
  437. }
  438. };
  439. function prepareNewTile(tile, terrainProvider, imageryLayerCollection) {
  440. let available = terrainProvider.getTileDataAvailable(
  441. tile.x,
  442. tile.y,
  443. tile.level
  444. );
  445. if (!defined(available) && defined(tile.parent)) {
  446. // Provider doesn't know if this tile is available. Does the parent tile know?
  447. const parent = tile.parent;
  448. const parentSurfaceTile = parent.data;
  449. if (defined(parentSurfaceTile) && defined(parentSurfaceTile.terrainData)) {
  450. available = parentSurfaceTile.terrainData.isChildAvailable(
  451. parent.x,
  452. parent.y,
  453. tile.x,
  454. tile.y
  455. );
  456. }
  457. }
  458. if (available === false) {
  459. // This tile is not available, so mark it failed so we start upsampling right away.
  460. tile.data.terrainState = TerrainState.FAILED;
  461. }
  462. // Map imagery tiles to this terrain tile
  463. for (let i = 0, len = imageryLayerCollection.length; i < len; ++i) {
  464. const layer = imageryLayerCollection.get(i);
  465. if (layer.show) {
  466. layer._createTileImagerySkeletons(tile, terrainProvider);
  467. }
  468. }
  469. }
  470. function processTerrainStateMachine(
  471. tile,
  472. frameState,
  473. terrainProvider,
  474. imageryLayerCollection,
  475. quadtree,
  476. vertexArraysToDestroy
  477. ) {
  478. const surfaceTile = tile.data;
  479. // If this tile is FAILED, we'll need to upsample from the parent. If the parent isn't
  480. // ready for that, let's push it along.
  481. const parent = tile.parent;
  482. if (
  483. surfaceTile.terrainState === TerrainState.FAILED &&
  484. parent !== undefined
  485. ) {
  486. const parentReady =
  487. parent.data !== undefined &&
  488. parent.data.terrainData !== undefined &&
  489. parent.data.terrainData.canUpsample !== false;
  490. if (!parentReady) {
  491. GlobeSurfaceTile.processStateMachine(
  492. parent,
  493. frameState,
  494. terrainProvider,
  495. imageryLayerCollection,
  496. quadtree,
  497. vertexArraysToDestroy,
  498. true
  499. );
  500. }
  501. }
  502. if (surfaceTile.terrainState === TerrainState.FAILED) {
  503. upsample(
  504. surfaceTile,
  505. tile,
  506. frameState,
  507. terrainProvider,
  508. tile.x,
  509. tile.y,
  510. tile.level
  511. );
  512. }
  513. if (surfaceTile.terrainState === TerrainState.UNLOADED) {
  514. requestTileGeometry(
  515. surfaceTile,
  516. terrainProvider,
  517. tile.x,
  518. tile.y,
  519. tile.level
  520. );
  521. }
  522. if (surfaceTile.terrainState === TerrainState.RECEIVED) {
  523. transform(
  524. surfaceTile,
  525. frameState,
  526. terrainProvider,
  527. tile.x,
  528. tile.y,
  529. tile.level
  530. );
  531. }
  532. if (surfaceTile.terrainState === TerrainState.TRANSFORMED) {
  533. createResources(
  534. surfaceTile,
  535. frameState.context,
  536. terrainProvider,
  537. tile.x,
  538. tile.y,
  539. tile.level,
  540. vertexArraysToDestroy
  541. );
  542. // Update the tile's exaggeration in case the globe's exaggeration changed while the tile was being processed
  543. surfaceTile.updateExaggeration(tile, frameState, quadtree);
  544. }
  545. if (
  546. surfaceTile.terrainState >= TerrainState.RECEIVED &&
  547. surfaceTile.waterMaskTexture === undefined &&
  548. terrainProvider.hasWaterMask
  549. ) {
  550. const terrainData = surfaceTile.terrainData;
  551. if (terrainData.waterMask !== undefined) {
  552. createWaterMaskTextureIfNeeded(frameState.context, surfaceTile);
  553. } else {
  554. const sourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
  555. if (defined(sourceTile) && defined(sourceTile.data.waterMaskTexture)) {
  556. surfaceTile.waterMaskTexture = sourceTile.data.waterMaskTexture;
  557. ++surfaceTile.waterMaskTexture.referenceCount;
  558. surfaceTile._computeWaterMaskTranslationAndScale(
  559. tile,
  560. sourceTile,
  561. surfaceTile.waterMaskTranslationAndScale
  562. );
  563. }
  564. }
  565. }
  566. }
  567. function upsample(surfaceTile, tile, frameState, terrainProvider, x, y, level) {
  568. const parent = tile.parent;
  569. if (!parent) {
  570. // Trying to upsample from a root tile. No can do. This tile is a failure.
  571. tile.state = QuadtreeTileLoadState.FAILED;
  572. return;
  573. }
  574. const sourceData = parent.data.terrainData;
  575. const sourceX = parent.x;
  576. const sourceY = parent.y;
  577. const sourceLevel = parent.level;
  578. if (!defined(sourceData)) {
  579. // Parent is not available, so we can't upsample this tile yet.
  580. return;
  581. }
  582. const terrainDataPromise = sourceData.upsample(
  583. terrainProvider.tilingScheme,
  584. sourceX,
  585. sourceY,
  586. sourceLevel,
  587. x,
  588. y,
  589. level
  590. );
  591. if (!defined(terrainDataPromise)) {
  592. // The upsample request has been deferred - try again later.
  593. return;
  594. }
  595. surfaceTile.terrainState = TerrainState.RECEIVING;
  596. Promise.resolve(terrainDataPromise)
  597. .then(function (terrainData) {
  598. surfaceTile.terrainData = terrainData;
  599. surfaceTile.terrainState = TerrainState.RECEIVED;
  600. })
  601. .catch(function () {
  602. surfaceTile.terrainState = TerrainState.FAILED;
  603. });
  604. }
  605. function requestTileGeometry(surfaceTile, terrainProvider, x, y, level) {
  606. function success(terrainData) {
  607. surfaceTile.terrainData = terrainData;
  608. surfaceTile.terrainState = TerrainState.RECEIVED;
  609. surfaceTile.request = undefined;
  610. }
  611. function failure(error) {
  612. if (surfaceTile.request.state === RequestState.CANCELLED) {
  613. // Cancelled due to low priority - try again later.
  614. surfaceTile.terrainData = undefined;
  615. surfaceTile.terrainState = TerrainState.UNLOADED;
  616. surfaceTile.request = undefined;
  617. return;
  618. }
  619. // Initially assume failure. reportError may retry, in which case the state will
  620. // change to RECEIVING or UNLOADED.
  621. surfaceTile.terrainState = TerrainState.FAILED;
  622. surfaceTile.request = undefined;
  623. const message = `Failed to obtain terrain tile X: ${x} Y: ${y} Level: ${level}. Error message: "${error}"`;
  624. terrainProvider._requestError = TileProviderError.reportError(
  625. terrainProvider._requestError,
  626. terrainProvider,
  627. terrainProvider.errorEvent,
  628. message,
  629. x,
  630. y,
  631. level
  632. );
  633. if (terrainProvider._requestError.retry) {
  634. doRequest();
  635. }
  636. }
  637. function doRequest() {
  638. // Request the terrain from the terrain provider.
  639. const request = new Request({
  640. throttle: false,
  641. throttleByServer: true,
  642. type: RequestType.TERRAIN,
  643. });
  644. surfaceTile.request = request;
  645. const requestPromise = terrainProvider.requestTileGeometry(
  646. x,
  647. y,
  648. level,
  649. request
  650. );
  651. // If the request method returns undefined (instead of a promise), the request
  652. // has been deferred.
  653. if (defined(requestPromise)) {
  654. surfaceTile.terrainState = TerrainState.RECEIVING;
  655. Promise.resolve(requestPromise)
  656. .then(function (terrainData) {
  657. success(terrainData);
  658. })
  659. .catch(function (e) {
  660. failure(e);
  661. });
  662. } else {
  663. // Deferred - try again later.
  664. surfaceTile.terrainState = TerrainState.UNLOADED;
  665. surfaceTile.request = undefined;
  666. }
  667. }
  668. doRequest();
  669. }
  670. const scratchCreateMeshOptions = {
  671. tilingScheme: undefined,
  672. x: 0,
  673. y: 0,
  674. level: 0,
  675. exaggeration: 1.0,
  676. exaggerationRelativeHeight: 0.0,
  677. throttle: true,
  678. };
  679. function transform(surfaceTile, frameState, terrainProvider, x, y, level) {
  680. const tilingScheme = terrainProvider.tilingScheme;
  681. const createMeshOptions = scratchCreateMeshOptions;
  682. createMeshOptions.tilingScheme = tilingScheme;
  683. createMeshOptions.x = x;
  684. createMeshOptions.y = y;
  685. createMeshOptions.level = level;
  686. createMeshOptions.exaggeration = frameState.terrainExaggeration;
  687. createMeshOptions.exaggerationRelativeHeight =
  688. frameState.terrainExaggerationRelativeHeight;
  689. createMeshOptions.throttle = true;
  690. const terrainData = surfaceTile.terrainData;
  691. const meshPromise = terrainData.createMesh(createMeshOptions);
  692. if (!defined(meshPromise)) {
  693. // Postponed.
  694. return;
  695. }
  696. surfaceTile.terrainState = TerrainState.TRANSFORMING;
  697. Promise.resolve(meshPromise)
  698. .then(function (mesh) {
  699. surfaceTile.mesh = mesh;
  700. surfaceTile.terrainState = TerrainState.TRANSFORMED;
  701. })
  702. .catch(function () {
  703. surfaceTile.terrainState = TerrainState.FAILED;
  704. });
  705. }
  706. GlobeSurfaceTile._createVertexArrayForMesh = function (context, mesh) {
  707. const typedArray = mesh.vertices;
  708. const buffer = Buffer.createVertexBuffer({
  709. context: context,
  710. typedArray: typedArray,
  711. usage: BufferUsage.STATIC_DRAW,
  712. });
  713. const attributes = mesh.encoding.getAttributes(buffer);
  714. const indexBuffers = mesh.indices.indexBuffers || {};
  715. let indexBuffer = indexBuffers[context.id];
  716. if (!defined(indexBuffer) || indexBuffer.isDestroyed()) {
  717. const indices = mesh.indices;
  718. indexBuffer = Buffer.createIndexBuffer({
  719. context: context,
  720. typedArray: indices,
  721. usage: BufferUsage.STATIC_DRAW,
  722. indexDatatype: IndexDatatype.fromSizeInBytes(indices.BYTES_PER_ELEMENT),
  723. });
  724. indexBuffer.vertexArrayDestroyable = false;
  725. indexBuffer.referenceCount = 1;
  726. indexBuffers[context.id] = indexBuffer;
  727. mesh.indices.indexBuffers = indexBuffers;
  728. } else {
  729. ++indexBuffer.referenceCount;
  730. }
  731. return new VertexArray({
  732. context: context,
  733. attributes: attributes,
  734. indexBuffer: indexBuffer,
  735. });
  736. };
  737. GlobeSurfaceTile._freeVertexArray = function (vertexArray) {
  738. if (defined(vertexArray)) {
  739. const indexBuffer = vertexArray.indexBuffer;
  740. if (!vertexArray.isDestroyed()) {
  741. vertexArray.destroy();
  742. }
  743. if (
  744. defined(indexBuffer) &&
  745. !indexBuffer.isDestroyed() &&
  746. defined(indexBuffer.referenceCount)
  747. ) {
  748. --indexBuffer.referenceCount;
  749. if (indexBuffer.referenceCount === 0) {
  750. indexBuffer.destroy();
  751. }
  752. }
  753. }
  754. };
  755. function createResources(
  756. surfaceTile,
  757. context,
  758. terrainProvider,
  759. x,
  760. y,
  761. level,
  762. vertexArraysToDestroy
  763. ) {
  764. surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  765. context,
  766. surfaceTile.mesh
  767. );
  768. surfaceTile.terrainState = TerrainState.READY;
  769. surfaceTile.fill =
  770. surfaceTile.fill && surfaceTile.fill.destroy(vertexArraysToDestroy);
  771. }
  772. function getContextWaterMaskData(context) {
  773. let data = context.cache.tile_waterMaskData;
  774. if (!defined(data)) {
  775. const allWaterTexture = Texture.create({
  776. context: context,
  777. pixelFormat: PixelFormat.LUMINANCE,
  778. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  779. source: {
  780. arrayBufferView: new Uint8Array([255]),
  781. width: 1,
  782. height: 1,
  783. },
  784. });
  785. allWaterTexture.referenceCount = 1;
  786. const sampler = new Sampler({
  787. wrapS: TextureWrap.CLAMP_TO_EDGE,
  788. wrapT: TextureWrap.CLAMP_TO_EDGE,
  789. minificationFilter: TextureMinificationFilter.LINEAR,
  790. magnificationFilter: TextureMagnificationFilter.LINEAR,
  791. });
  792. data = {
  793. allWaterTexture: allWaterTexture,
  794. sampler: sampler,
  795. destroy: function () {
  796. this.allWaterTexture.destroy();
  797. },
  798. };
  799. context.cache.tile_waterMaskData = data;
  800. }
  801. return data;
  802. }
  803. function createWaterMaskTextureIfNeeded(context, surfaceTile) {
  804. const waterMask = surfaceTile.terrainData.waterMask;
  805. const waterMaskData = getContextWaterMaskData(context);
  806. let texture;
  807. const waterMaskLength = waterMask.length;
  808. if (waterMaskLength === 1) {
  809. // Length 1 means the tile is entirely land or entirely water.
  810. // A value of 0 indicates entirely land, a value of 1 indicates entirely water.
  811. if (waterMask[0] !== 0) {
  812. texture = waterMaskData.allWaterTexture;
  813. } else {
  814. // Leave the texture undefined if the tile is entirely land.
  815. return;
  816. }
  817. } else {
  818. const textureSize = Math.sqrt(waterMaskLength);
  819. texture = Texture.create({
  820. context: context,
  821. pixelFormat: PixelFormat.LUMINANCE,
  822. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  823. source: {
  824. width: textureSize,
  825. height: textureSize,
  826. arrayBufferView: waterMask,
  827. },
  828. sampler: waterMaskData.sampler,
  829. flipY: false,
  830. });
  831. texture.referenceCount = 0;
  832. }
  833. ++texture.referenceCount;
  834. surfaceTile.waterMaskTexture = texture;
  835. Cartesian4.fromElements(
  836. 0.0,
  837. 0.0,
  838. 1.0,
  839. 1.0,
  840. surfaceTile.waterMaskTranslationAndScale
  841. );
  842. }
  843. GlobeSurfaceTile.prototype._findAncestorTileWithTerrainData = function (tile) {
  844. let sourceTile = tile.parent;
  845. while (
  846. defined(sourceTile) &&
  847. (!defined(sourceTile.data) ||
  848. !defined(sourceTile.data.terrainData) ||
  849. sourceTile.data.terrainData.wasCreatedByUpsampling())
  850. ) {
  851. sourceTile = sourceTile.parent;
  852. }
  853. return sourceTile;
  854. };
  855. GlobeSurfaceTile.prototype._computeWaterMaskTranslationAndScale = function (
  856. tile,
  857. sourceTile,
  858. result
  859. ) {
  860. const sourceTileRectangle = sourceTile.rectangle;
  861. const tileRectangle = tile.rectangle;
  862. const tileWidth = tileRectangle.width;
  863. const tileHeight = tileRectangle.height;
  864. const scaleX = tileWidth / sourceTileRectangle.width;
  865. const scaleY = tileHeight / sourceTileRectangle.height;
  866. result.x =
  867. (scaleX * (tileRectangle.west - sourceTileRectangle.west)) / tileWidth;
  868. result.y =
  869. (scaleY * (tileRectangle.south - sourceTileRectangle.south)) / tileHeight;
  870. result.z = scaleX;
  871. result.w = scaleY;
  872. return result;
  873. };
  874. export default GlobeSurfaceTile;