TerrainEncoding.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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(
  33. axisAlignedBoundingBox,
  34. minimumHeight,
  35. maximumHeight,
  36. fromENU,
  37. hasVertexNormals,
  38. hasWebMercatorT
  39. ) {
  40. var quantization = TerrainQuantization.NONE;
  41. var center;
  42. var toENU;
  43. var matrix;
  44. if (
  45. defined(axisAlignedBoundingBox) &&
  46. defined(minimumHeight) &&
  47. defined(maximumHeight) &&
  48. defined(fromENU)
  49. ) {
  50. var minimum = axisAlignedBoundingBox.minimum;
  51. var maximum = axisAlignedBoundingBox.maximum;
  52. var dimensions = Cartesian3.subtract(
  53. maximum,
  54. minimum,
  55. cartesian3DimScratch
  56. );
  57. var hDim = maximumHeight - minimumHeight;
  58. var maxDim = Math.max(Cartesian3.maximumComponent(dimensions), hDim);
  59. if (maxDim < SHIFT_LEFT_12 - 1.0) {
  60. quantization = TerrainQuantization.BITS12;
  61. } else {
  62. quantization = TerrainQuantization.NONE;
  63. }
  64. center = axisAlignedBoundingBox.center;
  65. toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());
  66. var translation = Cartesian3.negate(minimum, cartesian3Scratch);
  67. Matrix4.multiply(
  68. Matrix4.fromTranslation(translation, matrix4Scratch),
  69. toENU,
  70. toENU
  71. );
  72. var scale = cartesian3Scratch;
  73. scale.x = 1.0 / dimensions.x;
  74. scale.y = 1.0 / dimensions.y;
  75. scale.z = 1.0 / dimensions.z;
  76. Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);
  77. matrix = Matrix4.clone(fromENU);
  78. Matrix4.setTranslation(matrix, Cartesian3.ZERO, matrix);
  79. fromENU = Matrix4.clone(fromENU, new Matrix4());
  80. var translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch);
  81. var scaleMatrix = Matrix4.fromScale(dimensions, matrix4Scratch2);
  82. var st = Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch);
  83. Matrix4.multiply(fromENU, st, fromENU);
  84. Matrix4.multiply(matrix, st, matrix);
  85. }
  86. /**
  87. * How the vertices of the mesh were compressed.
  88. * @type {TerrainQuantization}
  89. */
  90. this.quantization = quantization;
  91. /**
  92. * The minimum height of the tile including the skirts.
  93. * @type {Number}
  94. */
  95. this.minimumHeight = minimumHeight;
  96. /**
  97. * The maximum height of the tile.
  98. * @type {Number}
  99. */
  100. this.maximumHeight = maximumHeight;
  101. /**
  102. * The center of the tile.
  103. * @type {Cartesian3}
  104. */
  105. this.center = center;
  106. /**
  107. * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales
  108. * it so each component is in the [0, 1] range.
  109. * @type {Matrix4}
  110. */
  111. this.toScaledENU = toENU;
  112. /**
  113. * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame
  114. * @type {Matrix4}
  115. */
  116. this.fromScaledENU = fromENU;
  117. /**
  118. * The matrix used to decompress the terrain vertices in the shader for RTE rendering.
  119. * @type {Matrix4}
  120. */
  121. this.matrix = matrix;
  122. /**
  123. * The terrain mesh contains normals.
  124. * @type {Boolean}
  125. */
  126. this.hasVertexNormals = hasVertexNormals;
  127. /**
  128. * The terrain mesh contains a vertical texture coordinate following the Web Mercator projection.
  129. * @type {Boolean}
  130. */
  131. this.hasWebMercatorT = defaultValue(hasWebMercatorT, false);
  132. }
  133. TerrainEncoding.prototype.encode = function (
  134. vertexBuffer,
  135. bufferIndex,
  136. position,
  137. uv,
  138. height,
  139. normalToPack,
  140. webMercatorT
  141. ) {
  142. var u = uv.x;
  143. var v = uv.y;
  144. if (this.quantization === TerrainQuantization.BITS12) {
  145. position = Matrix4.multiplyByPoint(
  146. this.toScaledENU,
  147. position,
  148. cartesian3Scratch
  149. );
  150. position.x = CesiumMath.clamp(position.x, 0.0, 1.0);
  151. position.y = CesiumMath.clamp(position.y, 0.0, 1.0);
  152. position.z = CesiumMath.clamp(position.z, 0.0, 1.0);
  153. var hDim = this.maximumHeight - this.minimumHeight;
  154. var h = CesiumMath.clamp((height - this.minimumHeight) / hDim, 0.0, 1.0);
  155. Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);
  156. var compressed0 = AttributeCompression.compressTextureCoordinates(
  157. cartesian2Scratch
  158. );
  159. Cartesian2.fromElements(position.z, h, cartesian2Scratch);
  160. var compressed1 = AttributeCompression.compressTextureCoordinates(
  161. cartesian2Scratch
  162. );
  163. Cartesian2.fromElements(u, v, cartesian2Scratch);
  164. var compressed2 = AttributeCompression.compressTextureCoordinates(
  165. cartesian2Scratch
  166. );
  167. vertexBuffer[bufferIndex++] = compressed0;
  168. vertexBuffer[bufferIndex++] = compressed1;
  169. vertexBuffer[bufferIndex++] = compressed2;
  170. if (this.hasWebMercatorT) {
  171. Cartesian2.fromElements(webMercatorT, 0.0, cartesian2Scratch);
  172. var compressed3 = AttributeCompression.compressTextureCoordinates(
  173. cartesian2Scratch
  174. );
  175. vertexBuffer[bufferIndex++] = compressed3;
  176. }
  177. } else {
  178. Cartesian3.subtract(position, this.center, cartesian3Scratch);
  179. vertexBuffer[bufferIndex++] = cartesian3Scratch.x;
  180. vertexBuffer[bufferIndex++] = cartesian3Scratch.y;
  181. vertexBuffer[bufferIndex++] = cartesian3Scratch.z;
  182. vertexBuffer[bufferIndex++] = height;
  183. vertexBuffer[bufferIndex++] = u;
  184. vertexBuffer[bufferIndex++] = v;
  185. if (this.hasWebMercatorT) {
  186. vertexBuffer[bufferIndex++] = webMercatorT;
  187. }
  188. }
  189. if (this.hasVertexNormals) {
  190. vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat(
  191. normalToPack
  192. );
  193. }
  194. return bufferIndex;
  195. };
  196. TerrainEncoding.prototype.decodePosition = function (buffer, index, result) {
  197. if (!defined(result)) {
  198. result = new Cartesian3();
  199. }
  200. index *= this.getStride();
  201. if (this.quantization === TerrainQuantization.BITS12) {
  202. var xy = AttributeCompression.decompressTextureCoordinates(
  203. buffer[index],
  204. cartesian2Scratch
  205. );
  206. result.x = xy.x;
  207. result.y = xy.y;
  208. var zh = AttributeCompression.decompressTextureCoordinates(
  209. buffer[index + 1],
  210. cartesian2Scratch
  211. );
  212. result.z = zh.x;
  213. return Matrix4.multiplyByPoint(this.fromScaledENU, result, result);
  214. }
  215. result.x = buffer[index];
  216. result.y = buffer[index + 1];
  217. result.z = buffer[index + 2];
  218. return Cartesian3.add(result, this.center, result);
  219. };
  220. TerrainEncoding.prototype.decodeTextureCoordinates = function (
  221. buffer,
  222. index,
  223. result
  224. ) {
  225. if (!defined(result)) {
  226. result = new Cartesian2();
  227. }
  228. index *= this.getStride();
  229. if (this.quantization === TerrainQuantization.BITS12) {
  230. return AttributeCompression.decompressTextureCoordinates(
  231. buffer[index + 2],
  232. result
  233. );
  234. }
  235. return Cartesian2.fromElements(buffer[index + 4], buffer[index + 5], result);
  236. };
  237. TerrainEncoding.prototype.decodeHeight = function (buffer, index) {
  238. index *= this.getStride();
  239. if (this.quantization === TerrainQuantization.BITS12) {
  240. var zh = AttributeCompression.decompressTextureCoordinates(
  241. buffer[index + 1],
  242. cartesian2Scratch
  243. );
  244. return (
  245. zh.y * (this.maximumHeight - this.minimumHeight) + this.minimumHeight
  246. );
  247. }
  248. return buffer[index + 3];
  249. };
  250. TerrainEncoding.prototype.decodeWebMercatorT = function (buffer, index) {
  251. index *= this.getStride();
  252. if (this.quantization === TerrainQuantization.BITS12) {
  253. return AttributeCompression.decompressTextureCoordinates(
  254. buffer[index + 3],
  255. cartesian2Scratch
  256. ).x;
  257. }
  258. return buffer[index + 6];
  259. };
  260. TerrainEncoding.prototype.getOctEncodedNormal = function (
  261. buffer,
  262. index,
  263. result
  264. ) {
  265. var stride = this.getStride();
  266. index = (index + 1) * stride - 1;
  267. var temp = buffer[index] / 256.0;
  268. var x = Math.floor(temp);
  269. var y = (temp - x) * 256.0;
  270. return Cartesian2.fromElements(x, y, result);
  271. };
  272. TerrainEncoding.prototype.getStride = function () {
  273. var vertexStride;
  274. switch (this.quantization) {
  275. case TerrainQuantization.BITS12:
  276. vertexStride = 3;
  277. break;
  278. default:
  279. vertexStride = 6;
  280. }
  281. if (this.hasWebMercatorT) {
  282. ++vertexStride;
  283. }
  284. if (this.hasVertexNormals) {
  285. ++vertexStride;
  286. }
  287. return vertexStride;
  288. };
  289. var attributesNone = {
  290. position3DAndHeight: 0,
  291. textureCoordAndEncodedNormals: 1,
  292. };
  293. var attributes = {
  294. compressed0: 0,
  295. compressed1: 1,
  296. };
  297. TerrainEncoding.prototype.getAttributes = function (buffer) {
  298. var datatype = ComponentDatatype.FLOAT;
  299. var sizeInBytes = ComponentDatatype.getSizeInBytes(datatype);
  300. var stride;
  301. if (this.quantization === TerrainQuantization.NONE) {
  302. var position3DAndHeightLength = 4;
  303. var numTexCoordComponents = 2;
  304. if (this.hasWebMercatorT) {
  305. ++numTexCoordComponents;
  306. }
  307. if (this.hasVertexNormals) {
  308. ++numTexCoordComponents;
  309. }
  310. stride = (position3DAndHeightLength + numTexCoordComponents) * sizeInBytes;
  311. return [
  312. {
  313. index: attributesNone.position3DAndHeight,
  314. vertexBuffer: buffer,
  315. componentDatatype: datatype,
  316. componentsPerAttribute: position3DAndHeightLength,
  317. offsetInBytes: 0,
  318. strideInBytes: stride,
  319. },
  320. {
  321. index: attributesNone.textureCoordAndEncodedNormals,
  322. vertexBuffer: buffer,
  323. componentDatatype: datatype,
  324. componentsPerAttribute: numTexCoordComponents,
  325. offsetInBytes: position3DAndHeightLength * sizeInBytes,
  326. strideInBytes: stride,
  327. },
  328. ];
  329. }
  330. var numCompressed0 = 3;
  331. var numCompressed1 = 0;
  332. if (this.hasWebMercatorT || this.hasVertexNormals) {
  333. ++numCompressed0;
  334. }
  335. if (this.hasWebMercatorT && this.hasVertexNormals) {
  336. ++numCompressed1;
  337. stride = (numCompressed0 + numCompressed1) * sizeInBytes;
  338. return [
  339. {
  340. index: attributes.compressed0,
  341. vertexBuffer: buffer,
  342. componentDatatype: datatype,
  343. componentsPerAttribute: numCompressed0,
  344. offsetInBytes: 0,
  345. strideInBytes: stride,
  346. },
  347. {
  348. index: attributes.compressed1,
  349. vertexBuffer: buffer,
  350. componentDatatype: datatype,
  351. componentsPerAttribute: numCompressed1,
  352. offsetInBytes: numCompressed0 * sizeInBytes,
  353. strideInBytes: stride,
  354. },
  355. ];
  356. }
  357. return [
  358. {
  359. index: attributes.compressed0,
  360. vertexBuffer: buffer,
  361. componentDatatype: datatype,
  362. componentsPerAttribute: numCompressed0,
  363. },
  364. ];
  365. };
  366. TerrainEncoding.prototype.getAttributeLocations = function () {
  367. if (this.quantization === TerrainQuantization.NONE) {
  368. return attributesNone;
  369. }
  370. return attributes;
  371. };
  372. TerrainEncoding.clone = function (encoding, result) {
  373. if (!defined(result)) {
  374. result = new TerrainEncoding();
  375. }
  376. result.quantization = encoding.quantization;
  377. result.minimumHeight = encoding.minimumHeight;
  378. result.maximumHeight = encoding.maximumHeight;
  379. result.center = Cartesian3.clone(encoding.center);
  380. result.toScaledENU = Matrix4.clone(encoding.toScaledENU);
  381. result.fromScaledENU = Matrix4.clone(encoding.fromScaledENU);
  382. result.matrix = Matrix4.clone(encoding.matrix);
  383. result.hasVertexNormals = encoding.hasVertexNormals;
  384. result.hasWebMercatorT = encoding.hasWebMercatorT;
  385. return result;
  386. };
  387. export default TerrainEncoding;