TileTerrain.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/Cartesian3',
  5. '../Core/ComponentDatatype',
  6. '../Core/defined',
  7. '../Core/DeveloperError',
  8. '../Core/IndexDatatype',
  9. '../Core/TileProviderError',
  10. '../Renderer/BufferUsage',
  11. '../ThirdParty/when',
  12. './terrainAttributeLocations',
  13. './TerrainState'
  14. ], function(
  15. BoundingSphere,
  16. Cartesian3,
  17. ComponentDatatype,
  18. defined,
  19. DeveloperError,
  20. IndexDatatype,
  21. TileProviderError,
  22. BufferUsage,
  23. when,
  24. terrainAttributeLocations,
  25. TerrainState) {
  26. "use strict";
  27. /**
  28. * Manages details of the terrain load or upsample process.
  29. *
  30. * @alias TileTerrain
  31. * @constructor
  32. * @private
  33. *
  34. * @param {TerrainData} [upsampleDetails.data] The terrain data being upsampled.
  35. * @param {Number} [upsampleDetails.x] The X coordinate of the tile being upsampled.
  36. * @param {Number} [upsampleDetails.y] The Y coordinate of the tile being upsampled.
  37. * @param {Number} [upsampleDetails.level] The level coordinate of the tile being upsampled.
  38. */
  39. var TileTerrain = function TileTerrain(upsampleDetails) {
  40. /**
  41. * The current state of the terrain in the terrain processing pipeline.
  42. * @type {TerrainState}
  43. * @default {@link TerrainState.UNLOADED}
  44. */
  45. this.state = TerrainState.UNLOADED;
  46. this.data = undefined;
  47. this.mesh = undefined;
  48. this.vertexArray = undefined;
  49. this.upsampleDetails = upsampleDetails;
  50. };
  51. TileTerrain.prototype.freeResources = function() {
  52. this.state = TerrainState.UNLOADED;
  53. this.data = undefined;
  54. this.mesh = undefined;
  55. if (defined(this.vertexArray)) {
  56. var indexBuffer = this.vertexArray.indexBuffer;
  57. this.vertexArray.destroy();
  58. this.vertexArray = undefined;
  59. if (!indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
  60. --indexBuffer.referenceCount;
  61. if (indexBuffer.referenceCount === 0) {
  62. indexBuffer.destroy();
  63. }
  64. }
  65. }
  66. };
  67. TileTerrain.prototype.publishToTile = function(tile) {
  68. var surfaceTile = tile.data;
  69. var mesh = this.mesh;
  70. Cartesian3.clone(mesh.center, surfaceTile.center);
  71. surfaceTile.minimumHeight = mesh.minimumHeight;
  72. surfaceTile.maximumHeight = mesh.maximumHeight;
  73. surfaceTile.boundingSphere3D = BoundingSphere.clone(mesh.boundingSphere3D, surfaceTile.boundingSphere3D);
  74. tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace);
  75. // Free the tile's existing vertex array, if any.
  76. surfaceTile.freeVertexArray();
  77. // Transfer ownership of the vertex array to the tile itself.
  78. surfaceTile.vertexArray = this.vertexArray;
  79. this.vertexArray = undefined;
  80. };
  81. TileTerrain.prototype.processLoadStateMachine = function(context, terrainProvider, x, y, level) {
  82. if (this.state === TerrainState.UNLOADED) {
  83. requestTileGeometry(this, terrainProvider, x, y, level);
  84. }
  85. if (this.state === TerrainState.RECEIVED) {
  86. transform(this, context, terrainProvider, x, y, level);
  87. }
  88. if (this.state === TerrainState.TRANSFORMED) {
  89. createResources(this, context, terrainProvider, x, y, level);
  90. }
  91. };
  92. function requestTileGeometry(tileTerrain, terrainProvider, x, y, level) {
  93. function success(terrainData) {
  94. tileTerrain.data = terrainData;
  95. tileTerrain.state = TerrainState.RECEIVED;
  96. }
  97. function failure() {
  98. // Initially assume failure. handleError may retry, in which case the state will
  99. // change to RECEIVING or UNLOADED.
  100. tileTerrain.state = TerrainState.FAILED;
  101. var message = 'Failed to obtain terrain tile X: ' + x + ' Y: ' + y + ' Level: ' + level + '.';
  102. terrainProvider._requestError = TileProviderError.handleError(
  103. terrainProvider._requestError,
  104. terrainProvider,
  105. terrainProvider.errorEvent,
  106. message,
  107. x, y, level,
  108. doRequest);
  109. }
  110. function doRequest() {
  111. // Request the terrain from the terrain provider.
  112. tileTerrain.data = terrainProvider.requestTileGeometry(x, y, level);
  113. // If the request method returns undefined (instead of a promise), the request
  114. // has been deferred.
  115. if (defined(tileTerrain.data)) {
  116. tileTerrain.state = TerrainState.RECEIVING;
  117. when(tileTerrain.data, success, failure);
  118. } else {
  119. // Deferred - try again later.
  120. tileTerrain.state = TerrainState.UNLOADED;
  121. }
  122. }
  123. doRequest();
  124. }
  125. TileTerrain.prototype.processUpsampleStateMachine = function(context, terrainProvider, x, y, level) {
  126. if (this.state === TerrainState.UNLOADED) {
  127. var upsampleDetails = this.upsampleDetails;
  128. //>>includeStart('debug', pragmas.debug);
  129. if (!defined(upsampleDetails)) {
  130. throw new DeveloperError('TileTerrain cannot upsample unless provided upsampleDetails.');
  131. }
  132. //>>includeEnd('debug');
  133. var sourceData = upsampleDetails.data;
  134. var sourceX = upsampleDetails.x;
  135. var sourceY = upsampleDetails.y;
  136. var sourceLevel = upsampleDetails.level;
  137. this.data = sourceData.upsample(terrainProvider.tilingScheme, sourceX, sourceY, sourceLevel, x, y, level);
  138. if (!defined(this.data)) {
  139. // The upsample request has been deferred - try again later.
  140. return;
  141. }
  142. this.state = TerrainState.RECEIVING;
  143. var that = this;
  144. when(this.data, function(terrainData) {
  145. that.data = terrainData;
  146. that.state = TerrainState.RECEIVED;
  147. }, function() {
  148. that.state = TerrainState.FAILED;
  149. });
  150. }
  151. if (this.state === TerrainState.RECEIVED) {
  152. transform(this, context, terrainProvider, x, y, level);
  153. }
  154. if (this.state === TerrainState.TRANSFORMED) {
  155. createResources(this, context, terrainProvider, x, y, level);
  156. }
  157. };
  158. function transform(tileTerrain, context, terrainProvider, x, y, level) {
  159. var tilingScheme = terrainProvider.tilingScheme;
  160. var terrainData = tileTerrain.data;
  161. var meshPromise = terrainData.createMesh(tilingScheme, x, y, level);
  162. if (!defined(meshPromise)) {
  163. // Postponed.
  164. return;
  165. }
  166. tileTerrain.state = TerrainState.TRANSFORMING;
  167. when(meshPromise, function(mesh) {
  168. tileTerrain.mesh = mesh;
  169. tileTerrain.state = TerrainState.TRANSFORMED;
  170. }, function() {
  171. tileTerrain.state = TerrainState.FAILED;
  172. });
  173. }
  174. function createResources(tileTerrain, context, terrainProvider, x, y, level) {
  175. var datatype = ComponentDatatype.FLOAT;
  176. var stride;
  177. var numTexCoordComponents;
  178. var typedArray = tileTerrain.mesh.vertices;
  179. var buffer = context.createVertexBuffer(typedArray, BufferUsage.STATIC_DRAW);
  180. if (terrainProvider.hasVertexNormals) {
  181. stride = 7 * ComponentDatatype.getSizeInBytes(datatype);
  182. numTexCoordComponents = 3;
  183. } else {
  184. stride = 6 * ComponentDatatype.getSizeInBytes(datatype);
  185. numTexCoordComponents = 2;
  186. }
  187. var position3DAndHeightLength = 4;
  188. var attributes = [{
  189. index : terrainAttributeLocations.position3DAndHeight,
  190. vertexBuffer : buffer,
  191. componentDatatype : datatype,
  192. componentsPerAttribute : position3DAndHeightLength,
  193. offsetInBytes : 0,
  194. strideInBytes : stride
  195. }, {
  196. index : terrainAttributeLocations.textureCoordAndEncodedNormals,
  197. vertexBuffer : buffer,
  198. componentDatatype : datatype,
  199. componentsPerAttribute : numTexCoordComponents,
  200. offsetInBytes : position3DAndHeightLength * ComponentDatatype.getSizeInBytes(datatype),
  201. strideInBytes : stride
  202. }];
  203. var indexBuffers = tileTerrain.mesh.indices.indexBuffers || {};
  204. var indexBuffer = indexBuffers[context.id];
  205. if (!defined(indexBuffer) || indexBuffer.isDestroyed()) {
  206. var indices = tileTerrain.mesh.indices;
  207. var indexDatatype = (indices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT;
  208. indexBuffer = context.createIndexBuffer(indices, BufferUsage.STATIC_DRAW, indexDatatype);
  209. indexBuffer.vertexArrayDestroyable = false;
  210. indexBuffer.referenceCount = 1;
  211. indexBuffers[context.id] = indexBuffer;
  212. tileTerrain.mesh.indices.indexBuffers = indexBuffers;
  213. } else {
  214. ++indexBuffer.referenceCount;
  215. }
  216. tileTerrain.vertexArray = context.createVertexArray(attributes, indexBuffer);
  217. tileTerrain.state = TerrainState.READY;
  218. }
  219. return TileTerrain;
  220. });