TerrainEncoding.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. import AttributeCompression from './AttributeCompression.js';
  2. import Cartesian2 from './Cartesian2.js';
  3. import Cartesian3 from './Cartesian3.js';
  4. import ComponentDatatype from './ComponentDatatype.js';
  5. import defaultValue from './defaultValue.js';
  6. import defined from './defined.js';
  7. import CesiumMath from './Math.js';
  8. import Matrix4 from './Matrix4.js';
  9. import TerrainQuantization from './TerrainQuantization.js';
  10. var cartesian3Scratch = new Cartesian3();
  11. var cartesian3DimScratch = new Cartesian3();
  12. var cartesian2Scratch = new Cartesian2();
  13. var matrix4Scratch = new Matrix4();
  14. var matrix4Scratch2 = new Matrix4();
  15. var SHIFT_LEFT_12 = Math.pow(2.0, 12.0);
  16. /**
  17. * Data used to quantize and pack the terrain mesh. The position can be unpacked for picking and all attributes
  18. * are unpacked in the vertex shader.
  19. *
  20. * @alias TerrainEncoding
  21. * @constructor
  22. *
  23. * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center.
  24. * @param {Number} minimumHeight The minimum height.
  25. * @param {Number} maximumHeight The maximum height.
  26. * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh.
  27. * @param {Boolean} hasVertexNormals If the mesh has vertex normals.
  28. * @param {Boolean} [hasWebMercatorT=false] true if the terrain data includes a Web Mercator texture coordinate; otherwise, false.
  29. *
  30. * @private
  31. */
  32. function TerrainEncoding(axisAlignedBoundingBox, minimumHeight, maximumHeight, fromENU, hasVertexNormals, hasWebMercatorT) {
  33. var quantization = TerrainQuantization.NONE;
  34. var center;
  35. var toENU;
  36. var matrix;
  37. if (defined(axisAlignedBoundingBox) && defined(minimumHeight) && defined(maximumHeight) && defined(fromENU)) {
  38. var minimum = axisAlignedBoundingBox.minimum;
  39. var maximum = axisAlignedBoundingBox.maximum;
  40. var dimensions = Cartesian3.subtract(maximum, minimum, cartesian3DimScratch);
  41. var hDim = maximumHeight - minimumHeight;
  42. var maxDim = Math.max(Cartesian3.maximumComponent(dimensions), hDim);
  43. if (maxDim < SHIFT_LEFT_12 - 1.0) {
  44. quantization = TerrainQuantization.BITS12;
  45. } else {
  46. quantization = TerrainQuantization.NONE;
  47. }
  48. center = axisAlignedBoundingBox.center;
  49. toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());
  50. var translation = Cartesian3.negate(minimum, cartesian3Scratch);
  51. Matrix4.multiply(Matrix4.fromTranslation(translation, matrix4Scratch), toENU, toENU);
  52. var scale = cartesian3Scratch;
  53. scale.x = 1.0 / dimensions.x;
  54. scale.y = 1.0 / dimensions.y;
  55. scale.z = 1.0 / dimensions.z;
  56. Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);
  57. matrix = Matrix4.clone(fromENU);
  58. Matrix4.setTranslation(matrix, Cartesian3.ZERO, matrix);
  59. fromENU = Matrix4.clone(fromENU, new Matrix4());
  60. var translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch);
  61. var scaleMatrix = Matrix4.fromScale(dimensions, matrix4Scratch2);
  62. var st = Matrix4.multiply(translationMatrix, scaleMatrix,matrix4Scratch);
  63. Matrix4.multiply(fromENU, st, fromENU);
  64. Matrix4.multiply(matrix, st, matrix);
  65. }
  66. /**
  67. * How the vertices of the mesh were compressed.
  68. * @type {TerrainQuantization}
  69. */
  70. this.quantization = quantization;
  71. /**
  72. * The minimum height of the tile including the skirts.
  73. * @type {Number}
  74. */
  75. this.minimumHeight = minimumHeight;
  76. /**
  77. * The maximum height of the tile.
  78. * @type {Number}
  79. */
  80. this.maximumHeight = maximumHeight;
  81. /**
  82. * The center of the tile.
  83. * @type {Cartesian3}
  84. */
  85. this.center = center;
  86. /**
  87. * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales
  88. * it so each component is in the [0, 1] range.
  89. * @type {Matrix4}
  90. */
  91. this.toScaledENU = toENU;
  92. /**
  93. * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame
  94. * @type {Matrix4}
  95. */
  96. this.fromScaledENU = fromENU;
  97. /**
  98. * The matrix used to decompress the terrain vertices in the shader for RTE rendering.
  99. * @type {Matrix4}
  100. */
  101. this.matrix = matrix;
  102. /**
  103. * The terrain mesh contains normals.
  104. * @type {Boolean}
  105. */
  106. this.hasVertexNormals = hasVertexNormals;
  107. /**
  108. * The terrain mesh contains a vertical texture coordinate following the Web Mercator projection.
  109. * @type {Boolean}
  110. */
  111. this.hasWebMercatorT = defaultValue(hasWebMercatorT, false);
  112. }
  113. TerrainEncoding.prototype.encode = function(vertexBuffer, bufferIndex, position, uv, height, normalToPack, webMercatorT) {
  114. var u = uv.x;
  115. var v = uv.y;
  116. if (this.quantization === TerrainQuantization.BITS12) {
  117. position = Matrix4.multiplyByPoint(this.toScaledENU, position, cartesian3Scratch);
  118. position.x = CesiumMath.clamp(position.x, 0.0, 1.0);
  119. position.y = CesiumMath.clamp(position.y, 0.0, 1.0);
  120. position.z = CesiumMath.clamp(position.z, 0.0, 1.0);
  121. var hDim = this.maximumHeight - this.minimumHeight;
  122. var h = CesiumMath.clamp((height - this.minimumHeight) / hDim, 0.0, 1.0);
  123. Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);
  124. var compressed0 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);
  125. Cartesian2.fromElements(position.z, h, cartesian2Scratch);
  126. var compressed1 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);
  127. Cartesian2.fromElements(u, v, cartesian2Scratch);
  128. var compressed2 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);
  129. vertexBuffer[bufferIndex++] = compressed0;
  130. vertexBuffer[bufferIndex++] = compressed1;
  131. vertexBuffer[bufferIndex++] = compressed2;
  132. if (this.hasWebMercatorT) {
  133. Cartesian2.fromElements(webMercatorT, 0.0, cartesian2Scratch);
  134. var compressed3 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);
  135. vertexBuffer[bufferIndex++] = compressed3;
  136. }
  137. } else {
  138. Cartesian3.subtract(position, this.center, cartesian3Scratch);
  139. vertexBuffer[bufferIndex++] = cartesian3Scratch.x;
  140. vertexBuffer[bufferIndex++] = cartesian3Scratch.y;
  141. vertexBuffer[bufferIndex++] = cartesian3Scratch.z;
  142. vertexBuffer[bufferIndex++] = height;
  143. vertexBuffer[bufferIndex++] = u;
  144. vertexBuffer[bufferIndex++] = v;
  145. if (this.hasWebMercatorT) {
  146. vertexBuffer[bufferIndex++] = webMercatorT;
  147. }
  148. }
  149. if (this.hasVertexNormals) {
  150. vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat(normalToPack);
  151. }
  152. return bufferIndex;
  153. };
  154. TerrainEncoding.prototype.decodePosition = function(buffer, index, result) {
  155. if (!defined(result)) {
  156. result = new Cartesian3();
  157. }
  158. index *= this.getStride();
  159. if (this.quantization === TerrainQuantization.BITS12) {
  160. var xy = AttributeCompression.decompressTextureCoordinates(buffer[index], cartesian2Scratch);
  161. result.x = xy.x;
  162. result.y = xy.y;
  163. var zh = AttributeCompression.decompressTextureCoordinates(buffer[index + 1], cartesian2Scratch);
  164. result.z = zh.x;
  165. return Matrix4.multiplyByPoint(this.fromScaledENU, result, result);
  166. }
  167. result.x = buffer[index];
  168. result.y = buffer[index + 1];
  169. result.z = buffer[index + 2];
  170. return Cartesian3.add(result, this.center, result);
  171. };
  172. TerrainEncoding.prototype.decodeTextureCoordinates = function(buffer, index, result) {
  173. if (!defined(result)) {
  174. result = new Cartesian2();
  175. }
  176. index *= this.getStride();
  177. if (this.quantization === TerrainQuantization.BITS12) {
  178. return AttributeCompression.decompressTextureCoordinates(buffer[index + 2], result);
  179. }
  180. return Cartesian2.fromElements(buffer[index + 4], buffer[index + 5], result);
  181. };
  182. TerrainEncoding.prototype.decodeHeight = function(buffer, index) {
  183. index *= this.getStride();
  184. if (this.quantization === TerrainQuantization.BITS12) {
  185. var zh = AttributeCompression.decompressTextureCoordinates(buffer[index + 1], cartesian2Scratch);
  186. return zh.y * (this.maximumHeight - this.minimumHeight) + this.minimumHeight;
  187. }
  188. return buffer[index + 3];
  189. };
  190. TerrainEncoding.prototype.decodeWebMercatorT = function(buffer, index) {
  191. index *= this.getStride();
  192. if (this.quantization === TerrainQuantization.BITS12) {
  193. return AttributeCompression.decompressTextureCoordinates(buffer[index + 3], cartesian2Scratch).x;
  194. }
  195. return buffer[index + 6];
  196. };
  197. TerrainEncoding.prototype.getOctEncodedNormal = function(buffer, index, result) {
  198. var stride = this.getStride();
  199. index = (index + 1) * stride - 1;
  200. var temp = buffer[index] / 256.0;
  201. var x = Math.floor(temp);
  202. var y = (temp - x) * 256.0;
  203. return Cartesian2.fromElements(x, y, result);
  204. };
  205. TerrainEncoding.prototype.getStride = function() {
  206. var vertexStride;
  207. switch (this.quantization) {
  208. case TerrainQuantization.BITS12:
  209. vertexStride = 3;
  210. break;
  211. default:
  212. vertexStride = 6;
  213. }
  214. if (this.hasWebMercatorT) {
  215. ++vertexStride;
  216. }
  217. if (this.hasVertexNormals) {
  218. ++vertexStride;
  219. }
  220. return vertexStride;
  221. };
  222. var attributesNone = {
  223. position3DAndHeight : 0,
  224. textureCoordAndEncodedNormals : 1
  225. };
  226. var attributes = {
  227. compressed0 : 0,
  228. compressed1 : 1
  229. };
  230. TerrainEncoding.prototype.getAttributes = function(buffer) {
  231. var datatype = ComponentDatatype.FLOAT;
  232. var sizeInBytes = ComponentDatatype.getSizeInBytes(datatype);
  233. var stride;
  234. if (this.quantization === TerrainQuantization.NONE) {
  235. var position3DAndHeightLength = 4;
  236. var numTexCoordComponents = 2;
  237. if (this.hasWebMercatorT) {
  238. ++numTexCoordComponents;
  239. }
  240. if (this.hasVertexNormals) {
  241. ++numTexCoordComponents;
  242. }
  243. stride = (position3DAndHeightLength + numTexCoordComponents) * sizeInBytes;
  244. return [{
  245. index : attributesNone.position3DAndHeight,
  246. vertexBuffer : buffer,
  247. componentDatatype : datatype,
  248. componentsPerAttribute : position3DAndHeightLength,
  249. offsetInBytes : 0,
  250. strideInBytes : stride
  251. }, {
  252. index : attributesNone.textureCoordAndEncodedNormals,
  253. vertexBuffer : buffer,
  254. componentDatatype : datatype,
  255. componentsPerAttribute : numTexCoordComponents,
  256. offsetInBytes : position3DAndHeightLength * sizeInBytes,
  257. strideInBytes : stride
  258. }];
  259. }
  260. var numCompressed0 = 3;
  261. var numCompressed1 = 0;
  262. if (this.hasWebMercatorT || this.hasVertexNormals) {
  263. ++numCompressed0;
  264. }
  265. if (this.hasWebMercatorT && this.hasVertexNormals) {
  266. ++numCompressed1;
  267. stride = (numCompressed0 + numCompressed1) * sizeInBytes;
  268. return [{
  269. index : attributes.compressed0,
  270. vertexBuffer : buffer,
  271. componentDatatype : datatype,
  272. componentsPerAttribute : numCompressed0,
  273. offsetInBytes : 0,
  274. strideInBytes : stride
  275. }, {
  276. index : attributes.compressed1,
  277. vertexBuffer : buffer,
  278. componentDatatype : datatype,
  279. componentsPerAttribute : numCompressed1,
  280. offsetInBytes : numCompressed0 * sizeInBytes,
  281. strideInBytes : stride
  282. }];
  283. }
  284. return [{
  285. index : attributes.compressed0,
  286. vertexBuffer : buffer,
  287. componentDatatype : datatype,
  288. componentsPerAttribute : numCompressed0
  289. }];
  290. };
  291. TerrainEncoding.prototype.getAttributeLocations = function() {
  292. if (this.quantization === TerrainQuantization.NONE) {
  293. return attributesNone;
  294. }
  295. return attributes;
  296. };
  297. TerrainEncoding.clone = function(encoding, result) {
  298. if (!defined(result)) {
  299. result = new TerrainEncoding();
  300. }
  301. result.quantization = encoding.quantization;
  302. result.minimumHeight = encoding.minimumHeight;
  303. result.maximumHeight = encoding.maximumHeight;
  304. result.center = Cartesian3.clone(encoding.center);
  305. result.toScaledENU = Matrix4.clone(encoding.toScaledENU);
  306. result.fromScaledENU = Matrix4.clone(encoding.fromScaledENU);
  307. result.matrix = Matrix4.clone(encoding.matrix);
  308. result.hasVertexNormals = encoding.hasVertexNormals;
  309. result.hasWebMercatorT = encoding.hasWebMercatorT;
  310. return result;
  311. };
  312. export default TerrainEncoding;