GlobeSurfaceTile.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  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. if (imageryLayer.imageryProvider.ready) {
  301. // Remove the placeholder and add the actual skeletons (if any)
  302. // at the same position. Then continue the loop at the same index.
  303. tileImagery.freeResources();
  304. tileImageryCollection.splice(i, 1);
  305. imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i);
  306. --i;
  307. len = tileImageryCollection.length;
  308. continue;
  309. } else {
  310. isUpsampledOnly = false;
  311. }
  312. }
  313. const thisTileDoneLoading = tileImagery.processStateMachine(
  314. tile,
  315. frameState,
  316. skipLoading
  317. );
  318. isDoneLoading = isDoneLoading && thisTileDoneLoading;
  319. // The imagery is renderable as soon as we have any renderable imagery for this region.
  320. isAnyTileLoaded =
  321. isAnyTileLoaded ||
  322. thisTileDoneLoading ||
  323. defined(tileImagery.readyImagery);
  324. isUpsampledOnly =
  325. isUpsampledOnly &&
  326. defined(tileImagery.loadingImagery) &&
  327. (tileImagery.loadingImagery.state === ImageryState.FAILED ||
  328. tileImagery.loadingImagery.state === ImageryState.INVALID);
  329. }
  330. tile.upsampledFromParent = isUpsampledOnly;
  331. // Allow rendering if any available layers are loaded
  332. tile.renderable = tile.renderable && (isAnyTileLoaded || isDoneLoading);
  333. return isDoneLoading;
  334. };
  335. function toggleGeodeticSurfaceNormals(
  336. surfaceTile,
  337. enabled,
  338. ellipsoid,
  339. frameState
  340. ) {
  341. const renderedMesh = surfaceTile.renderedMesh;
  342. const vertexBuffer = renderedMesh.vertices;
  343. const encoding = renderedMesh.encoding;
  344. const vertexCount = vertexBuffer.length / encoding.stride;
  345. // Calculate the new stride and generate a new buffer
  346. // Clone the other encoding, toggle geodetic surface normals, then clone again to get updated stride
  347. let newEncoding = TerrainEncoding.clone(encoding);
  348. newEncoding.hasGeodeticSurfaceNormals = enabled;
  349. newEncoding = TerrainEncoding.clone(newEncoding);
  350. const newStride = newEncoding.stride;
  351. const newVertexBuffer = new Float32Array(vertexCount * newStride);
  352. if (enabled) {
  353. encoding.addGeodeticSurfaceNormals(
  354. vertexBuffer,
  355. newVertexBuffer,
  356. ellipsoid
  357. );
  358. } else {
  359. encoding.removeGeodeticSurfaceNormals(vertexBuffer, newVertexBuffer);
  360. }
  361. renderedMesh.vertices = newVertexBuffer;
  362. renderedMesh.stride = newStride;
  363. // delete the old vertex array (which deletes the vertex buffer attached to it), and create a new vertex array with the new vertex buffer
  364. const isFill = renderedMesh !== surfaceTile.mesh;
  365. if (isFill) {
  366. GlobeSurfaceTile._freeVertexArray(surfaceTile.fill.vertexArray);
  367. surfaceTile.fill.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  368. frameState.context,
  369. renderedMesh
  370. );
  371. } else {
  372. GlobeSurfaceTile._freeVertexArray(surfaceTile.vertexArray);
  373. surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  374. frameState.context,
  375. renderedMesh
  376. );
  377. }
  378. GlobeSurfaceTile._freeVertexArray(surfaceTile.wireframeVertexArray);
  379. surfaceTile.wireframeVertexArray = undefined;
  380. }
  381. GlobeSurfaceTile.prototype.addGeodeticSurfaceNormals = function (
  382. ellipsoid,
  383. frameState
  384. ) {
  385. toggleGeodeticSurfaceNormals(this, true, ellipsoid, frameState);
  386. };
  387. GlobeSurfaceTile.prototype.removeGeodeticSurfaceNormals = function (
  388. frameState
  389. ) {
  390. toggleGeodeticSurfaceNormals(this, false, undefined, frameState);
  391. };
  392. GlobeSurfaceTile.prototype.updateExaggeration = function (
  393. tile,
  394. frameState,
  395. quadtree
  396. ) {
  397. const surfaceTile = this;
  398. const mesh = surfaceTile.renderedMesh;
  399. if (mesh === undefined) {
  400. return;
  401. }
  402. // Check the tile's terrain encoding to see if it has been exaggerated yet
  403. const exaggeration = frameState.terrainExaggeration;
  404. const exaggerationRelativeHeight =
  405. frameState.terrainExaggerationRelativeHeight;
  406. const hasExaggerationScale = exaggeration !== 1.0;
  407. const encoding = mesh.encoding;
  408. const encodingExaggerationScaleChanged =
  409. encoding.exaggeration !== exaggeration;
  410. const encodingRelativeHeightChanged =
  411. encoding.exaggerationRelativeHeight !== exaggerationRelativeHeight;
  412. if (encodingExaggerationScaleChanged || encodingRelativeHeightChanged) {
  413. // Turning exaggeration scale on/off requires adding or removing geodetic surface normals
  414. // Relative height only translates, so it has no effect on normals
  415. if (encodingExaggerationScaleChanged) {
  416. if (hasExaggerationScale && !encoding.hasGeodeticSurfaceNormals) {
  417. const ellipsoid = tile.tilingScheme.ellipsoid;
  418. surfaceTile.addGeodeticSurfaceNormals(ellipsoid, frameState);
  419. } else if (!hasExaggerationScale && encoding.hasGeodeticSurfaceNormals) {
  420. surfaceTile.removeGeodeticSurfaceNormals(frameState);
  421. }
  422. }
  423. encoding.exaggeration = exaggeration;
  424. encoding.exaggerationRelativeHeight = exaggerationRelativeHeight;
  425. // Notify the quadtree that this tile's height has changed
  426. if (quadtree !== undefined) {
  427. quadtree._tileToUpdateHeights.push(tile);
  428. const customData = tile.customData;
  429. const customDataLength = customData.length;
  430. for (let i = 0; i < customDataLength; i++) {
  431. // Restart the level so that a height update is triggered
  432. const data = customData[i];
  433. data.level = -1;
  434. }
  435. }
  436. }
  437. };
  438. function prepareNewTile(tile, terrainProvider, imageryLayerCollection) {
  439. let available = terrainProvider.getTileDataAvailable(
  440. tile.x,
  441. tile.y,
  442. tile.level
  443. );
  444. if (!defined(available) && defined(tile.parent)) {
  445. // Provider doesn't know if this tile is available. Does the parent tile know?
  446. const parent = tile.parent;
  447. const parentSurfaceTile = parent.data;
  448. if (defined(parentSurfaceTile) && defined(parentSurfaceTile.terrainData)) {
  449. available = parentSurfaceTile.terrainData.isChildAvailable(
  450. parent.x,
  451. parent.y,
  452. tile.x,
  453. tile.y
  454. );
  455. }
  456. }
  457. if (available === false) {
  458. // This tile is not available, so mark it failed so we start upsampling right away.
  459. tile.data.terrainState = TerrainState.FAILED;
  460. }
  461. // Map imagery tiles to this terrain tile
  462. for (let i = 0, len = imageryLayerCollection.length; i < len; ++i) {
  463. const layer = imageryLayerCollection.get(i);
  464. if (layer.show) {
  465. layer._createTileImagerySkeletons(tile, terrainProvider);
  466. }
  467. }
  468. }
  469. function processTerrainStateMachine(
  470. tile,
  471. frameState,
  472. terrainProvider,
  473. imageryLayerCollection,
  474. quadtree,
  475. vertexArraysToDestroy
  476. ) {
  477. const surfaceTile = tile.data;
  478. // If this tile is FAILED, we'll need to upsample from the parent. If the parent isn't
  479. // ready for that, let's push it along.
  480. const parent = tile.parent;
  481. if (
  482. surfaceTile.terrainState === TerrainState.FAILED &&
  483. parent !== undefined
  484. ) {
  485. const parentReady =
  486. parent.data !== undefined &&
  487. parent.data.terrainData !== undefined &&
  488. parent.data.terrainData.canUpsample !== false;
  489. if (!parentReady) {
  490. GlobeSurfaceTile.processStateMachine(
  491. parent,
  492. frameState,
  493. terrainProvider,
  494. imageryLayerCollection,
  495. quadtree,
  496. vertexArraysToDestroy,
  497. true
  498. );
  499. }
  500. }
  501. if (surfaceTile.terrainState === TerrainState.FAILED) {
  502. upsample(
  503. surfaceTile,
  504. tile,
  505. frameState,
  506. terrainProvider,
  507. tile.x,
  508. tile.y,
  509. tile.level
  510. );
  511. }
  512. if (surfaceTile.terrainState === TerrainState.UNLOADED) {
  513. requestTileGeometry(
  514. surfaceTile,
  515. terrainProvider,
  516. tile.x,
  517. tile.y,
  518. tile.level
  519. );
  520. }
  521. if (surfaceTile.terrainState === TerrainState.RECEIVED) {
  522. transform(
  523. surfaceTile,
  524. frameState,
  525. terrainProvider,
  526. tile.x,
  527. tile.y,
  528. tile.level
  529. );
  530. }
  531. if (surfaceTile.terrainState === TerrainState.TRANSFORMED) {
  532. createResources(
  533. surfaceTile,
  534. frameState.context,
  535. terrainProvider,
  536. tile.x,
  537. tile.y,
  538. tile.level,
  539. vertexArraysToDestroy
  540. );
  541. // Update the tile's exaggeration in case the globe's exaggeration changed while the tile was being processed
  542. surfaceTile.updateExaggeration(tile, frameState, quadtree);
  543. }
  544. if (
  545. surfaceTile.terrainState >= TerrainState.RECEIVED &&
  546. surfaceTile.waterMaskTexture === undefined &&
  547. terrainProvider.hasWaterMask
  548. ) {
  549. const terrainData = surfaceTile.terrainData;
  550. if (terrainData.waterMask !== undefined) {
  551. createWaterMaskTextureIfNeeded(frameState.context, surfaceTile);
  552. } else {
  553. const sourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
  554. if (defined(sourceTile) && defined(sourceTile.data.waterMaskTexture)) {
  555. surfaceTile.waterMaskTexture = sourceTile.data.waterMaskTexture;
  556. ++surfaceTile.waterMaskTexture.referenceCount;
  557. surfaceTile._computeWaterMaskTranslationAndScale(
  558. tile,
  559. sourceTile,
  560. surfaceTile.waterMaskTranslationAndScale
  561. );
  562. }
  563. }
  564. }
  565. }
  566. function upsample(surfaceTile, tile, frameState, terrainProvider, x, y, level) {
  567. const parent = tile.parent;
  568. if (!parent) {
  569. // Trying to upsample from a root tile. No can do. This tile is a failure.
  570. tile.state = QuadtreeTileLoadState.FAILED;
  571. return;
  572. }
  573. const sourceData = parent.data.terrainData;
  574. const sourceX = parent.x;
  575. const sourceY = parent.y;
  576. const sourceLevel = parent.level;
  577. if (!defined(sourceData)) {
  578. // Parent is not available, so we can't upsample this tile yet.
  579. return;
  580. }
  581. const terrainDataPromise = sourceData.upsample(
  582. terrainProvider.tilingScheme,
  583. sourceX,
  584. sourceY,
  585. sourceLevel,
  586. x,
  587. y,
  588. level
  589. );
  590. if (!defined(terrainDataPromise)) {
  591. // The upsample request has been deferred - try again later.
  592. return;
  593. }
  594. surfaceTile.terrainState = TerrainState.RECEIVING;
  595. Promise.resolve(terrainDataPromise)
  596. .then(function (terrainData) {
  597. surfaceTile.terrainData = terrainData;
  598. surfaceTile.terrainState = TerrainState.RECEIVED;
  599. })
  600. .catch(function () {
  601. surfaceTile.terrainState = TerrainState.FAILED;
  602. });
  603. }
  604. function requestTileGeometry(surfaceTile, terrainProvider, x, y, level) {
  605. function success(terrainData) {
  606. surfaceTile.terrainData = terrainData;
  607. surfaceTile.terrainState = TerrainState.RECEIVED;
  608. surfaceTile.request = undefined;
  609. }
  610. function failure(error) {
  611. if (surfaceTile.request.state === RequestState.CANCELLED) {
  612. // Cancelled due to low priority - try again later.
  613. surfaceTile.terrainData = undefined;
  614. surfaceTile.terrainState = TerrainState.UNLOADED;
  615. surfaceTile.request = undefined;
  616. return;
  617. }
  618. // Initially assume failure. handleError may retry, in which case the state will
  619. // change to RECEIVING or UNLOADED.
  620. surfaceTile.terrainState = TerrainState.FAILED;
  621. surfaceTile.request = undefined;
  622. const message = `Failed to obtain terrain tile X: ${x} Y: ${y} Level: ${level}. Error message: "${error}"`;
  623. terrainProvider._requestError = TileProviderError.handleError(
  624. terrainProvider._requestError,
  625. terrainProvider,
  626. terrainProvider.errorEvent,
  627. message,
  628. x,
  629. y,
  630. level,
  631. doRequest
  632. );
  633. }
  634. function doRequest() {
  635. // Request the terrain from the terrain provider.
  636. const request = new Request({
  637. throttle: false,
  638. throttleByServer: true,
  639. type: RequestType.TERRAIN,
  640. });
  641. surfaceTile.request = request;
  642. const requestPromise = terrainProvider.requestTileGeometry(
  643. x,
  644. y,
  645. level,
  646. request
  647. );
  648. // If the request method returns undefined (instead of a promise), the request
  649. // has been deferred.
  650. if (defined(requestPromise)) {
  651. surfaceTile.terrainState = TerrainState.RECEIVING;
  652. Promise.resolve(requestPromise)
  653. .then(function (terrainData) {
  654. success(terrainData);
  655. })
  656. .catch(function (e) {
  657. failure(e);
  658. });
  659. } else {
  660. // Deferred - try again later.
  661. surfaceTile.terrainState = TerrainState.UNLOADED;
  662. surfaceTile.request = undefined;
  663. }
  664. }
  665. doRequest();
  666. }
  667. const scratchCreateMeshOptions = {
  668. tilingScheme: undefined,
  669. x: 0,
  670. y: 0,
  671. level: 0,
  672. exaggeration: 1.0,
  673. exaggerationRelativeHeight: 0.0,
  674. throttle: true,
  675. };
  676. function transform(surfaceTile, frameState, terrainProvider, x, y, level) {
  677. const tilingScheme = terrainProvider.tilingScheme;
  678. const createMeshOptions = scratchCreateMeshOptions;
  679. createMeshOptions.tilingScheme = tilingScheme;
  680. createMeshOptions.x = x;
  681. createMeshOptions.y = y;
  682. createMeshOptions.level = level;
  683. createMeshOptions.exaggeration = frameState.terrainExaggeration;
  684. createMeshOptions.exaggerationRelativeHeight =
  685. frameState.terrainExaggerationRelativeHeight;
  686. createMeshOptions.throttle = true;
  687. const terrainData = surfaceTile.terrainData;
  688. const meshPromise = terrainData.createMesh(createMeshOptions);
  689. if (!defined(meshPromise)) {
  690. // Postponed.
  691. return;
  692. }
  693. surfaceTile.terrainState = TerrainState.TRANSFORMING;
  694. Promise.resolve(meshPromise)
  695. .then(function (mesh) {
  696. surfaceTile.mesh = mesh;
  697. surfaceTile.terrainState = TerrainState.TRANSFORMED;
  698. })
  699. .catch(function () {
  700. surfaceTile.terrainState = TerrainState.FAILED;
  701. });
  702. }
  703. GlobeSurfaceTile._createVertexArrayForMesh = function (context, mesh) {
  704. const typedArray = mesh.vertices;
  705. const buffer = Buffer.createVertexBuffer({
  706. context: context,
  707. typedArray: typedArray,
  708. usage: BufferUsage.STATIC_DRAW,
  709. });
  710. const attributes = mesh.encoding.getAttributes(buffer);
  711. const indexBuffers = mesh.indices.indexBuffers || {};
  712. let indexBuffer = indexBuffers[context.id];
  713. if (!defined(indexBuffer) || indexBuffer.isDestroyed()) {
  714. const indices = mesh.indices;
  715. indexBuffer = Buffer.createIndexBuffer({
  716. context: context,
  717. typedArray: indices,
  718. usage: BufferUsage.STATIC_DRAW,
  719. indexDatatype: IndexDatatype.fromSizeInBytes(indices.BYTES_PER_ELEMENT),
  720. });
  721. indexBuffer.vertexArrayDestroyable = false;
  722. indexBuffer.referenceCount = 1;
  723. indexBuffers[context.id] = indexBuffer;
  724. mesh.indices.indexBuffers = indexBuffers;
  725. } else {
  726. ++indexBuffer.referenceCount;
  727. }
  728. return new VertexArray({
  729. context: context,
  730. attributes: attributes,
  731. indexBuffer: indexBuffer,
  732. });
  733. };
  734. GlobeSurfaceTile._freeVertexArray = function (vertexArray) {
  735. if (defined(vertexArray)) {
  736. const indexBuffer = vertexArray.indexBuffer;
  737. if (!vertexArray.isDestroyed()) {
  738. vertexArray.destroy();
  739. }
  740. if (
  741. defined(indexBuffer) &&
  742. !indexBuffer.isDestroyed() &&
  743. defined(indexBuffer.referenceCount)
  744. ) {
  745. --indexBuffer.referenceCount;
  746. if (indexBuffer.referenceCount === 0) {
  747. indexBuffer.destroy();
  748. }
  749. }
  750. }
  751. };
  752. function createResources(
  753. surfaceTile,
  754. context,
  755. terrainProvider,
  756. x,
  757. y,
  758. level,
  759. vertexArraysToDestroy
  760. ) {
  761. surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(
  762. context,
  763. surfaceTile.mesh
  764. );
  765. surfaceTile.terrainState = TerrainState.READY;
  766. surfaceTile.fill =
  767. surfaceTile.fill && surfaceTile.fill.destroy(vertexArraysToDestroy);
  768. }
  769. function getContextWaterMaskData(context) {
  770. let data = context.cache.tile_waterMaskData;
  771. if (!defined(data)) {
  772. const allWaterTexture = Texture.create({
  773. context: context,
  774. pixelFormat: PixelFormat.LUMINANCE,
  775. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  776. source: {
  777. arrayBufferView: new Uint8Array([255]),
  778. width: 1,
  779. height: 1,
  780. },
  781. });
  782. allWaterTexture.referenceCount = 1;
  783. const sampler = new Sampler({
  784. wrapS: TextureWrap.CLAMP_TO_EDGE,
  785. wrapT: TextureWrap.CLAMP_TO_EDGE,
  786. minificationFilter: TextureMinificationFilter.LINEAR,
  787. magnificationFilter: TextureMagnificationFilter.LINEAR,
  788. });
  789. data = {
  790. allWaterTexture: allWaterTexture,
  791. sampler: sampler,
  792. destroy: function () {
  793. this.allWaterTexture.destroy();
  794. },
  795. };
  796. context.cache.tile_waterMaskData = data;
  797. }
  798. return data;
  799. }
  800. function createWaterMaskTextureIfNeeded(context, surfaceTile) {
  801. const waterMask = surfaceTile.terrainData.waterMask;
  802. const waterMaskData = getContextWaterMaskData(context);
  803. let texture;
  804. const waterMaskLength = waterMask.length;
  805. if (waterMaskLength === 1) {
  806. // Length 1 means the tile is entirely land or entirely water.
  807. // A value of 0 indicates entirely land, a value of 1 indicates entirely water.
  808. if (waterMask[0] !== 0) {
  809. texture = waterMaskData.allWaterTexture;
  810. } else {
  811. // Leave the texture undefined if the tile is entirely land.
  812. return;
  813. }
  814. } else {
  815. const textureSize = Math.sqrt(waterMaskLength);
  816. texture = Texture.create({
  817. context: context,
  818. pixelFormat: PixelFormat.LUMINANCE,
  819. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  820. source: {
  821. width: textureSize,
  822. height: textureSize,
  823. arrayBufferView: waterMask,
  824. },
  825. sampler: waterMaskData.sampler,
  826. flipY: false,
  827. });
  828. texture.referenceCount = 0;
  829. }
  830. ++texture.referenceCount;
  831. surfaceTile.waterMaskTexture = texture;
  832. Cartesian4.fromElements(
  833. 0.0,
  834. 0.0,
  835. 1.0,
  836. 1.0,
  837. surfaceTile.waterMaskTranslationAndScale
  838. );
  839. }
  840. GlobeSurfaceTile.prototype._findAncestorTileWithTerrainData = function (tile) {
  841. let sourceTile = tile.parent;
  842. while (
  843. defined(sourceTile) &&
  844. (!defined(sourceTile.data) ||
  845. !defined(sourceTile.data.terrainData) ||
  846. sourceTile.data.terrainData.wasCreatedByUpsampling())
  847. ) {
  848. sourceTile = sourceTile.parent;
  849. }
  850. return sourceTile;
  851. };
  852. GlobeSurfaceTile.prototype._computeWaterMaskTranslationAndScale = function (
  853. tile,
  854. sourceTile,
  855. result
  856. ) {
  857. const sourceTileRectangle = sourceTile.rectangle;
  858. const tileRectangle = tile.rectangle;
  859. const tileWidth = tileRectangle.width;
  860. const tileHeight = tileRectangle.height;
  861. const scaleX = tileWidth / sourceTileRectangle.width;
  862. const scaleY = tileHeight / sourceTileRectangle.height;
  863. result.x =
  864. (scaleX * (tileRectangle.west - sourceTileRectangle.west)) / tileWidth;
  865. result.y =
  866. (scaleY * (tileRectangle.south - sourceTileRectangle.south)) / tileHeight;
  867. result.z = scaleX;
  868. result.w = scaleY;
  869. return result;
  870. };
  871. export default GlobeSurfaceTile;