GlobeSurfaceTile.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/Cartesian3',
  5. '../Core/Cartesian4',
  6. '../Core/Cartographic',
  7. '../Core/defaultValue',
  8. '../Core/defined',
  9. '../Core/defineProperties',
  10. '../Core/IntersectionTests',
  11. '../Core/PixelFormat',
  12. '../Core/Rectangle',
  13. '../Renderer/PixelDatatype',
  14. '../Renderer/TextureMagnificationFilter',
  15. '../Renderer/TextureMinificationFilter',
  16. '../Renderer/TextureWrap',
  17. './ImageryState',
  18. './QuadtreeTileLoadState',
  19. './SceneMode',
  20. './TerrainState',
  21. './TileTerrain'
  22. ], function(
  23. BoundingSphere,
  24. Cartesian3,
  25. Cartesian4,
  26. Cartographic,
  27. defaultValue,
  28. defined,
  29. defineProperties,
  30. IntersectionTests,
  31. PixelFormat,
  32. Rectangle,
  33. PixelDatatype,
  34. TextureMagnificationFilter,
  35. TextureMinificationFilter,
  36. TextureWrap,
  37. ImageryState,
  38. QuadtreeTileLoadState,
  39. SceneMode,
  40. TerrainState,
  41. TileTerrain) {
  42. "use strict";
  43. /**
  44. * Contains additional information about a {@link QuadtreeTile} of the globe's surface, and
  45. * encapsulates state transition logic for loading tiles.
  46. *
  47. * @constructor
  48. * @alias GlobeSurfaceTile
  49. * @private
  50. */
  51. var GlobeSurfaceTile = function() {
  52. /**
  53. * The {@link TileImagery} attached to this tile.
  54. * @type {TileImagery[]}
  55. * @default []
  56. */
  57. this.imagery = [];
  58. /**
  59. * The world coordinates of the southwest corner of the tile's rectangle.
  60. *
  61. * @type {Cartesian3}
  62. * @default Cartesian3()
  63. */
  64. this.southwestCornerCartesian = new Cartesian3();
  65. /**
  66. * The world coordinates of the northeast corner of the tile's rectangle.
  67. *
  68. * @type {Cartesian3}
  69. * @default Cartesian3()
  70. */
  71. this.northeastCornerCartesian = new Cartesian3();
  72. /**
  73. * A normal that, along with southwestCornerCartesian, defines a plane at the western edge of
  74. * the tile. Any position above (in the direction of the normal) this plane is outside the tile.
  75. *
  76. * @type {Cartesian3}
  77. * @default Cartesian3()
  78. */
  79. this.westNormal = new Cartesian3();
  80. /**
  81. * A normal that, along with southwestCornerCartesian, defines a plane at the southern edge of
  82. * the tile. Any position above (in the direction of the normal) this plane is outside the tile.
  83. * Because points of constant latitude do not necessary lie in a plane, positions below this
  84. * plane are not necessarily inside the tile, but they are close.
  85. *
  86. * @type {Cartesian3}
  87. * @default Cartesian3()
  88. */
  89. this.southNormal = new Cartesian3();
  90. /**
  91. * A normal that, along with northeastCornerCartesian, defines a plane at the eastern edge of
  92. * the tile. Any position above (in the direction of the normal) this plane is outside the tile.
  93. *
  94. * @type {Cartesian3}
  95. * @default Cartesian3()
  96. */
  97. this.eastNormal = new Cartesian3();
  98. /**
  99. * A normal that, along with northeastCornerCartesian, defines a plane at the eastern edge of
  100. * the tile. Any position above (in the direction of the normal) this plane is outside the tile.
  101. * Because points of constant latitude do not necessary lie in a plane, positions below this
  102. * plane are not necessarily inside the tile, but they are close.
  103. *
  104. * @type {Cartesian3}
  105. * @default Cartesian3()
  106. */
  107. this.northNormal = new Cartesian3();
  108. this.waterMaskTexture = undefined;
  109. this.waterMaskTranslationAndScale = new Cartesian4(0.0, 0.0, 1.0, 1.0);
  110. this.terrainData = undefined;
  111. this.center = new Cartesian3();
  112. this.vertexArray = undefined;
  113. this.minimumHeight = 0.0;
  114. this.maximumHeight = 0.0;
  115. this.boundingSphere3D = new BoundingSphere();
  116. this.boundingSphere2D = new BoundingSphere();
  117. this.occludeePointInScaledSpace = new Cartesian3();
  118. this.loadedTerrain = undefined;
  119. this.upsampledTerrain = undefined;
  120. this.pickBoundingSphere = new BoundingSphere();
  121. this.pickTerrain = undefined;
  122. this.surfaceShader = undefined;
  123. };
  124. defineProperties(GlobeSurfaceTile.prototype, {
  125. /**
  126. * Gets a value indicating whether or not this tile is eligible to be unloaded.
  127. * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
  128. * such as a request for data, is in progress on it. A tile will never be
  129. * unloaded while it is needed for rendering, regardless of the value of this
  130. * property.
  131. * @memberof GlobeSurfaceTile.prototype
  132. * @type {Boolean}
  133. */
  134. eligibleForUnloading : {
  135. get : function() {
  136. // Do not remove tiles that are transitioning or that have
  137. // imagery that is transitioning.
  138. var loadedTerrain = this.loadedTerrain;
  139. var loadingIsTransitioning = defined(loadedTerrain) &&
  140. (loadedTerrain.state === TerrainState.RECEIVING || loadedTerrain.state === TerrainState.TRANSFORMING);
  141. var upsampledTerrain = this.upsampledTerrain;
  142. var upsamplingIsTransitioning = defined(upsampledTerrain) &&
  143. (upsampledTerrain.state === TerrainState.RECEIVING || upsampledTerrain.state === TerrainState.TRANSFORMING);
  144. var shouldRemoveTile = !loadingIsTransitioning && !upsamplingIsTransitioning;
  145. var imagery = this.imagery;
  146. for (var i = 0, len = imagery.length; shouldRemoveTile && i < len; ++i) {
  147. var tileImagery = imagery[i];
  148. shouldRemoveTile = !defined(tileImagery.loadingImagery) || tileImagery.loadingImagery.state !== ImageryState.TRANSITIONING;
  149. }
  150. return shouldRemoveTile;
  151. }
  152. }
  153. });
  154. function getPosition(tile, scene, vertices, stride, index, result) {
  155. Cartesian3.unpack(vertices, index * stride, result);
  156. Cartesian3.add(tile.center, result, result);
  157. if (defined(scene) && scene.mode !== SceneMode.SCENE3D) {
  158. var projection = scene.mapProjection;
  159. var ellipsoid = projection.ellipsoid;
  160. var positionCart = ellipsoid.cartesianToCartographic(result);
  161. projection.project(positionCart, result);
  162. Cartesian3.fromElements(result.z, result.x, result.y, result);
  163. }
  164. return result;
  165. }
  166. var scratchV0 = new Cartesian3();
  167. var scratchV1 = new Cartesian3();
  168. var scratchV2 = new Cartesian3();
  169. var scratchResult = new Cartesian3();
  170. GlobeSurfaceTile.prototype.pick = function(ray, scene, cullBackFaces, result) {
  171. var terrain = this.pickTerrain;
  172. if (!defined(terrain)) {
  173. return undefined;
  174. }
  175. var mesh = terrain.mesh;
  176. if (!defined(mesh)) {
  177. return undefined;
  178. }
  179. var vertices = mesh.vertices;
  180. var stride = mesh.stride;
  181. var indices = mesh.indices;
  182. var length = indices.length;
  183. for (var i = 0; i < length; i += 3) {
  184. var i0 = indices[i];
  185. var i1 = indices[i + 1];
  186. var i2 = indices[i + 2];
  187. var v0 = getPosition(this, scene, vertices, stride, i0, scratchV0);
  188. var v1 = getPosition(this, scene, vertices, stride, i1, scratchV1);
  189. var v2 = getPosition(this, scene, vertices, stride, i2, scratchV2);
  190. var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, cullBackFaces, scratchResult);
  191. if (defined(intersection)) {
  192. return Cartesian3.clone(intersection, result);
  193. }
  194. }
  195. return undefined;
  196. };
  197. GlobeSurfaceTile.prototype.freeResources = function() {
  198. if (defined(this.waterMaskTexture)) {
  199. --this.waterMaskTexture.referenceCount;
  200. if (this.waterMaskTexture.referenceCount === 0) {
  201. this.waterMaskTexture.destroy();
  202. }
  203. this.waterMaskTexture = undefined;
  204. }
  205. this.terrainData = undefined;
  206. if (defined(this.loadedTerrain)) {
  207. this.loadedTerrain.freeResources();
  208. this.loadedTerrain = undefined;
  209. }
  210. if (defined(this.upsampledTerrain)) {
  211. this.upsampledTerrain.freeResources();
  212. this.upsampledTerrain = undefined;
  213. }
  214. if (defined(this.pickTerrain)) {
  215. this.pickTerrain.freeResources();
  216. this.pickTerrain = undefined;
  217. }
  218. var i, len;
  219. var imageryList = this.imagery;
  220. for (i = 0, len = imageryList.length; i < len; ++i) {
  221. imageryList[i].freeResources();
  222. }
  223. this.imagery.length = 0;
  224. this.freeVertexArray();
  225. };
  226. GlobeSurfaceTile.prototype.freeVertexArray = function() {
  227. var indexBuffer;
  228. if (defined(this.vertexArray)) {
  229. indexBuffer = this.vertexArray.indexBuffer;
  230. this.vertexArray = this.vertexArray.destroy();
  231. if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
  232. --indexBuffer.referenceCount;
  233. if (indexBuffer.referenceCount === 0) {
  234. indexBuffer.destroy();
  235. }
  236. }
  237. }
  238. if (defined(this.wireframeVertexArray)) {
  239. indexBuffer = this.wireframeVertexArray.indexBuffer;
  240. this.wireframeVertexArray = this.wireframeVertexArray.destroy();
  241. if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
  242. --indexBuffer.referenceCount;
  243. if (indexBuffer.referenceCount === 0) {
  244. indexBuffer.destroy();
  245. }
  246. }
  247. }
  248. };
  249. GlobeSurfaceTile.processStateMachine = function(tile, context, terrainProvider, imageryLayerCollection) {
  250. var surfaceTile = tile.data;
  251. if (!defined(surfaceTile)) {
  252. surfaceTile = tile.data = new GlobeSurfaceTile();
  253. }
  254. if (tile.state === QuadtreeTileLoadState.START) {
  255. prepareNewTile(tile, terrainProvider, imageryLayerCollection);
  256. tile.state = QuadtreeTileLoadState.LOADING;
  257. }
  258. if (tile.state === QuadtreeTileLoadState.LOADING) {
  259. processTerrainStateMachine(tile, context, terrainProvider);
  260. }
  261. // The terrain is renderable as soon as we have a valid vertex array.
  262. var isRenderable = defined(surfaceTile.vertexArray);
  263. // But it's not done loading until our two state machines are terminated.
  264. var isDoneLoading = !defined(surfaceTile.loadedTerrain) && !defined(surfaceTile.upsampledTerrain);
  265. // If this tile's terrain and imagery are just upsampled from its parent, mark the tile as
  266. // upsampled only. We won't refine a tile if its four children are upsampled only.
  267. var isUpsampledOnly = defined(surfaceTile.terrainData) && surfaceTile.terrainData.wasCreatedByUpsampling();
  268. // Transition imagery states
  269. var tileImageryCollection = surfaceTile.imagery;
  270. for (var i = 0, len = tileImageryCollection.length; i < len; ++i) {
  271. var tileImagery = tileImageryCollection[i];
  272. if (!defined(tileImagery.loadingImagery)) {
  273. isUpsampledOnly = false;
  274. continue;
  275. }
  276. if (tileImagery.loadingImagery.state === ImageryState.PLACEHOLDER) {
  277. var imageryLayer = tileImagery.loadingImagery.imageryLayer;
  278. if (imageryLayer.imageryProvider.ready) {
  279. // Remove the placeholder and add the actual skeletons (if any)
  280. // at the same position. Then continue the loop at the same index.
  281. tileImagery.freeResources();
  282. tileImageryCollection.splice(i, 1);
  283. imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i);
  284. --i;
  285. len = tileImageryCollection.length;
  286. continue;
  287. } else {
  288. isUpsampledOnly = false;
  289. }
  290. }
  291. var thisTileDoneLoading = tileImagery.processStateMachine(tile, context);
  292. isDoneLoading = isDoneLoading && thisTileDoneLoading;
  293. // The imagery is renderable as soon as we have any renderable imagery for this region.
  294. isRenderable = isRenderable && (thisTileDoneLoading || defined(tileImagery.readyImagery));
  295. isUpsampledOnly = isUpsampledOnly && defined(tileImagery.loadingImagery) &&
  296. (tileImagery.loadingImagery.state === ImageryState.FAILED || tileImagery.loadingImagery.state === ImageryState.INVALID);
  297. }
  298. tile.upsampledFromParent = isUpsampledOnly;
  299. // The tile becomes renderable when the terrain and all imagery data are loaded.
  300. if (i === len) {
  301. if (isRenderable) {
  302. tile.renderable = true;
  303. }
  304. if (isDoneLoading) {
  305. tile.state = QuadtreeTileLoadState.DONE;
  306. }
  307. }
  308. };
  309. var cartesian3Scratch = new Cartesian3();
  310. var cartesian3Scratch2 = new Cartesian3();
  311. var westernMidpointScratch = new Cartesian3();
  312. var easternMidpointScratch = new Cartesian3();
  313. var cartographicScratch = new Cartographic();
  314. function prepareNewTile(tile, terrainProvider, imageryLayerCollection) {
  315. var surfaceTile = tile.data;
  316. var upsampleTileDetails = getUpsampleTileDetails(tile);
  317. if (defined(upsampleTileDetails)) {
  318. surfaceTile.upsampledTerrain = new TileTerrain(upsampleTileDetails);
  319. }
  320. if (isDataAvailable(tile, terrainProvider)) {
  321. surfaceTile.loadedTerrain = new TileTerrain();
  322. }
  323. // Map imagery tiles to this terrain tile
  324. for (var i = 0, len = imageryLayerCollection.length; i < len; ++i) {
  325. var layer = imageryLayerCollection.get(i);
  326. if (layer.show) {
  327. layer._createTileImagerySkeletons(tile, terrainProvider);
  328. }
  329. }
  330. var ellipsoid = tile.tilingScheme.ellipsoid;
  331. // Compute tile rectangle boundaries for estimating the distance to the tile.
  332. var rectangle = tile.rectangle;
  333. ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle), surfaceTile.southwestCornerCartesian);
  334. ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle), surfaceTile.northeastCornerCartesian);
  335. // The middle latitude on the western edge.
  336. cartographicScratch.longitude = rectangle.west;
  337. cartographicScratch.latitude = (rectangle.south + rectangle.north) * 0.5;
  338. cartographicScratch.height = 0.0;
  339. var westernMidpointCartesian = ellipsoid.cartographicToCartesian(cartographicScratch, westernMidpointScratch);
  340. // Compute the normal of the plane on the western edge of the tile.
  341. var westNormal = Cartesian3.cross(westernMidpointCartesian, Cartesian3.UNIT_Z, cartesian3Scratch);
  342. Cartesian3.normalize(westNormal, surfaceTile.westNormal);
  343. // The middle latitude on the eastern edge.
  344. cartographicScratch.longitude = rectangle.east;
  345. var easternMidpointCartesian = ellipsoid.cartographicToCartesian(cartographicScratch, easternMidpointScratch);
  346. // Compute the normal of the plane on the eastern edge of the tile.
  347. var eastNormal = Cartesian3.cross(Cartesian3.UNIT_Z, easternMidpointCartesian, cartesian3Scratch);
  348. Cartesian3.normalize(eastNormal, surfaceTile.eastNormal);
  349. // Compute the normal of the plane bounding the southern edge of the tile.
  350. var southeastCornerNormal = ellipsoid.geodeticSurfaceNormalCartographic(Rectangle.southeast(rectangle), cartesian3Scratch2);
  351. var westVector = Cartesian3.subtract(westernMidpointCartesian, easternMidpointCartesian, cartesian3Scratch);
  352. var southNormal = Cartesian3.cross(southeastCornerNormal, westVector, cartesian3Scratch2);
  353. Cartesian3.normalize(southNormal, surfaceTile.southNormal);
  354. // Compute the normal of the plane bounding the northern edge of the tile.
  355. var northwestCornerNormal = ellipsoid.geodeticSurfaceNormalCartographic(Rectangle.northwest(rectangle), cartesian3Scratch2);
  356. var northNormal = Cartesian3.cross(westVector, northwestCornerNormal, cartesian3Scratch2);
  357. Cartesian3.normalize(northNormal, surfaceTile.northNormal);
  358. }
  359. function processTerrainStateMachine(tile, context, terrainProvider) {
  360. var surfaceTile = tile.data;
  361. var loaded = surfaceTile.loadedTerrain;
  362. var upsampled = surfaceTile.upsampledTerrain;
  363. var suspendUpsampling = false;
  364. if (defined(loaded)) {
  365. loaded.processLoadStateMachine(context, terrainProvider, tile.x, tile.y, tile.level);
  366. // Publish the terrain data on the tile as soon as it is available.
  367. // We'll potentially need it to upsample child tiles.
  368. if (loaded.state >= TerrainState.RECEIVED) {
  369. if (surfaceTile.terrainData !== loaded.data) {
  370. surfaceTile.terrainData = loaded.data;
  371. // If there's a water mask included in the terrain data, create a
  372. // texture for it.
  373. createWaterMaskTextureIfNeeded(context, surfaceTile);
  374. propagateNewLoadedDataToChildren(tile);
  375. }
  376. suspendUpsampling = true;
  377. }
  378. if (loaded.state === TerrainState.READY) {
  379. loaded.publishToTile(tile);
  380. // No further loading or upsampling is necessary.
  381. surfaceTile.pickTerrain = defaultValue(surfaceTile.loadedTerrain, surfaceTile.upsampledTerrain);
  382. surfaceTile.loadedTerrain = undefined;
  383. surfaceTile.upsampledTerrain = undefined;
  384. } else if (loaded.state === TerrainState.FAILED) {
  385. // Loading failed for some reason, or data is simply not available,
  386. // so no need to continue trying to load. Any retrying will happen before we
  387. // reach this point.
  388. surfaceTile.loadedTerrain = undefined;
  389. }
  390. }
  391. if (!suspendUpsampling && defined(upsampled)) {
  392. upsampled.processUpsampleStateMachine(context, terrainProvider, tile.x, tile.y, tile.level);
  393. // Publish the terrain data on the tile as soon as it is available.
  394. // We'll potentially need it to upsample child tiles.
  395. // It's safe to overwrite terrainData because we won't get here after
  396. // loaded terrain data has been received.
  397. if (upsampled.state >= TerrainState.RECEIVED) {
  398. if (surfaceTile.terrainData !== upsampled.data) {
  399. surfaceTile.terrainData = upsampled.data;
  400. // If the terrain provider has a water mask, "upsample" that as well
  401. // by computing texture translation and scale.
  402. if (terrainProvider.hasWaterMask) {
  403. upsampleWaterMask(tile);
  404. }
  405. propagateNewUpsampledDataToChildren(tile);
  406. }
  407. }
  408. if (upsampled.state === TerrainState.READY) {
  409. upsampled.publishToTile(tile);
  410. // No further upsampling is necessary. We need to continue loading, though.
  411. surfaceTile.pickTerrain = surfaceTile.upsampledTerrain;
  412. surfaceTile.upsampledTerrain = undefined;
  413. } else if (upsampled.state === TerrainState.FAILED) {
  414. // Upsampling failed for some reason. This is pretty much a catastrophic failure,
  415. // but maybe we'll be saved by loading.
  416. surfaceTile.upsampledTerrain = undefined;
  417. }
  418. }
  419. }
  420. function getUpsampleTileDetails(tile) {
  421. // Find the nearest ancestor with loaded terrain.
  422. var sourceTile = tile.parent;
  423. while (defined(sourceTile) && defined(sourceTile.data) && !defined(sourceTile.data.terrainData)) {
  424. sourceTile = sourceTile.parent;
  425. }
  426. if (!defined(sourceTile) || !defined(sourceTile.data)) {
  427. // No ancestors have loaded terrain - try again later.
  428. return undefined;
  429. }
  430. return {
  431. data : sourceTile.data.terrainData,
  432. x : sourceTile.x,
  433. y : sourceTile.y,
  434. level : sourceTile.level
  435. };
  436. }
  437. function propagateNewUpsampledDataToChildren(tile) {
  438. var surfaceTile = tile.data;
  439. // Now that there's new data for this tile:
  440. // - child tiles that were previously upsampled need to be re-upsampled based on the new data.
  441. // Generally this is only necessary when a child tile is upsampled, and then one
  442. // of its ancestors receives new (better) data and we want to re-upsample from the
  443. // new data.
  444. if (defined(tile._children)) {
  445. for (var childIndex = 0; childIndex < 4; ++childIndex) {
  446. var childTile = tile._children[childIndex];
  447. if (childTile.state !== QuadtreeTileLoadState.START) {
  448. var childSurfaceTile = childTile.data;
  449. if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) {
  450. // Data for the child tile has already been loaded.
  451. continue;
  452. }
  453. // Restart the upsampling process, no matter its current state.
  454. // We create a new instance rather than just restarting the existing one
  455. // because there could be an asynchronous operation pending on the existing one.
  456. if (defined(childSurfaceTile.upsampledTerrain)) {
  457. childSurfaceTile.upsampledTerrain.freeResources();
  458. }
  459. childSurfaceTile.upsampledTerrain = new TileTerrain({
  460. data : surfaceTile.terrainData,
  461. x : tile.x,
  462. y : tile.y,
  463. level : tile.level
  464. });
  465. childTile.state = QuadtreeTileLoadState.LOADING;
  466. }
  467. }
  468. }
  469. }
  470. function propagateNewLoadedDataToChildren(tile) {
  471. var surfaceTile = tile.data;
  472. // Now that there's new data for this tile:
  473. // - child tiles that were previously upsampled need to be re-upsampled based on the new data.
  474. // - child tiles that were previously deemed unavailable may now be available.
  475. if (defined(tile.children)) {
  476. for (var childIndex = 0; childIndex < 4; ++childIndex) {
  477. var childTile = tile.children[childIndex];
  478. if (childTile.state !== QuadtreeTileLoadState.START) {
  479. var childSurfaceTile = childTile.data;
  480. if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) {
  481. // Data for the child tile has already been loaded.
  482. continue;
  483. }
  484. // Restart the upsampling process, no matter its current state.
  485. // We create a new instance rather than just restarting the existing one
  486. // because there could be an asynchronous operation pending on the existing one.
  487. if (defined(childSurfaceTile.upsampledTerrain)) {
  488. childSurfaceTile.upsampledTerrain.freeResources();
  489. }
  490. childSurfaceTile.upsampledTerrain = new TileTerrain({
  491. data : surfaceTile.terrainData,
  492. x : tile.x,
  493. y : tile.y,
  494. level : tile.level
  495. });
  496. if (surfaceTile.terrainData.isChildAvailable(tile.x, tile.y, childTile.x, childTile.y)) {
  497. // Data is available for the child now. It might have been before, too.
  498. if (!defined(childSurfaceTile.loadedTerrain)) {
  499. // No load process is in progress, so start one.
  500. childSurfaceTile.loadedTerrain = new TileTerrain();
  501. }
  502. }
  503. childTile.state = QuadtreeTileLoadState.LOADING;
  504. }
  505. }
  506. }
  507. }
  508. function isDataAvailable(tile, terrainProvider) {
  509. var tileDataAvailable = terrainProvider.getTileDataAvailable(tile.x, tile.y, tile.level);
  510. if (defined(tileDataAvailable)) {
  511. return tileDataAvailable;
  512. }
  513. var parent = tile.parent;
  514. if (!defined(parent)) {
  515. // Data is assumed to be available for root tiles.
  516. return true;
  517. }
  518. if (!defined(parent.data) || !defined(parent.data.terrainData)) {
  519. // Parent tile data is not yet received or upsampled, so assume (for now) that this
  520. // child tile is not available.
  521. return false;
  522. }
  523. return parent.data.terrainData.isChildAvailable(parent.x, parent.y, tile.x, tile.y);
  524. }
  525. function getContextWaterMaskData(context) {
  526. var data = context.cache.tile_waterMaskData;
  527. if (!defined(data)) {
  528. var allWaterTexture = context.createTexture2D({
  529. pixelFormat : PixelFormat.LUMINANCE,
  530. pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
  531. source : {
  532. arrayBufferView : new Uint8Array([255]),
  533. width : 1,
  534. height : 1
  535. }
  536. });
  537. allWaterTexture.referenceCount = 1;
  538. var sampler = context.createSampler({
  539. wrapS : TextureWrap.CLAMP_TO_EDGE,
  540. wrapT : TextureWrap.CLAMP_TO_EDGE,
  541. minificationFilter : TextureMinificationFilter.LINEAR,
  542. magnificationFilter : TextureMagnificationFilter.LINEAR
  543. });
  544. data = {
  545. allWaterTexture : allWaterTexture,
  546. sampler : sampler,
  547. destroy : function() {
  548. this.allWaterTexture.destroy();
  549. }
  550. };
  551. context.cache.tile_waterMaskData = data;
  552. }
  553. return data;
  554. }
  555. function createWaterMaskTextureIfNeeded(context, surfaceTile) {
  556. var previousTexture = surfaceTile.waterMaskTexture;
  557. if (defined(previousTexture)) {
  558. --previousTexture.referenceCount;
  559. if (previousTexture.referenceCount === 0) {
  560. previousTexture.destroy();
  561. }
  562. surfaceTile.waterMaskTexture = undefined;
  563. }
  564. var waterMask = surfaceTile.terrainData.waterMask;
  565. if (!defined(waterMask)) {
  566. return;
  567. }
  568. var waterMaskData = getContextWaterMaskData(context);
  569. var texture;
  570. var waterMaskLength = waterMask.length;
  571. if (waterMaskLength === 1) {
  572. // Length 1 means the tile is entirely land or entirely water.
  573. // A value of 0 indicates entirely land, a value of 1 indicates entirely water.
  574. if (waterMask[0] !== 0) {
  575. texture = waterMaskData.allWaterTexture;
  576. } else {
  577. // Leave the texture undefined if the tile is entirely land.
  578. return;
  579. }
  580. } else {
  581. var textureSize = Math.sqrt(waterMaskLength);
  582. texture = context.createTexture2D({
  583. pixelFormat : PixelFormat.LUMINANCE,
  584. pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
  585. source : {
  586. width : textureSize,
  587. height : textureSize,
  588. arrayBufferView : waterMask
  589. }
  590. });
  591. texture.referenceCount = 0;
  592. texture.sampler = waterMaskData.sampler;
  593. }
  594. ++texture.referenceCount;
  595. surfaceTile.waterMaskTexture = texture;
  596. Cartesian4.fromElements(0.0, 0.0, 1.0, 1.0, surfaceTile.waterMaskTranslationAndScale);
  597. }
  598. function upsampleWaterMask(tile) {
  599. var surfaceTile = tile.data;
  600. // Find the nearest ancestor with loaded terrain.
  601. var sourceTile = tile.parent;
  602. while (defined(sourceTile) && !defined(sourceTile.data.terrainData) || sourceTile.data.terrainData.wasCreatedByUpsampling()) {
  603. sourceTile = sourceTile.parent;
  604. }
  605. if (!defined(sourceTile) || !defined(sourceTile.data.waterMaskTexture)) {
  606. // No ancestors have a water mask texture - try again later.
  607. return;
  608. }
  609. surfaceTile.waterMaskTexture = sourceTile.data.waterMaskTexture;
  610. ++surfaceTile.waterMaskTexture.referenceCount;
  611. // Compute the water mask translation and scale
  612. var sourceTileRectangle = sourceTile.rectangle;
  613. var tileRectangle = tile.rectangle;
  614. var tileWidth = tileRectangle.width;
  615. var tileHeight = tileRectangle.height;
  616. var scaleX = tileWidth / sourceTileRectangle.width;
  617. var scaleY = tileHeight / sourceTileRectangle.height;
  618. surfaceTile.waterMaskTranslationAndScale.x = scaleX * (tileRectangle.west - sourceTileRectangle.west) / tileWidth;
  619. surfaceTile.waterMaskTranslationAndScale.y = scaleY * (tileRectangle.south - sourceTileRectangle.south) / tileHeight;
  620. surfaceTile.waterMaskTranslationAndScale.z = scaleX;
  621. surfaceTile.waterMaskTranslationAndScale.w = scaleY;
  622. }
  623. return GlobeSurfaceTile;
  624. });