AttributeCompression.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. import Cartesian2 from "./Cartesian2.js";
  2. import Cartesian3 from "./Cartesian3.js";
  3. import Check from "./Check.js";
  4. import defined from "./defined.js";
  5. import DeveloperError from "./DeveloperError.js";
  6. import CesiumMath from "./Math.js";
  7. var RIGHT_SHIFT = 1.0 / 256.0;
  8. var LEFT_SHIFT = 256.0;
  9. /**
  10. * Attribute compression and decompression functions.
  11. *
  12. * @namespace AttributeCompression
  13. *
  14. * @private
  15. */
  16. var AttributeCompression = {};
  17. /**
  18. * Encodes a normalized vector into 2 SNORM values in the range of [0-rangeMax] following the 'oct' encoding.
  19. *
  20. * Oct encoding is a compact representation of unit length vectors.
  21. * The 'oct' encoding is described in "A Survey of Efficient Representations of Independent Unit Vectors",
  22. * Cigolle et al 2014: {@link http://jcgt.org/published/0003/02/01/}
  23. *
  24. * @param {Cartesian3} vector The normalized vector to be compressed into 2 component 'oct' encoding.
  25. * @param {Cartesian2} result The 2 component oct-encoded unit length vector.
  26. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits.
  27. * @returns {Cartesian2} The 2 component oct-encoded unit length vector.
  28. *
  29. * @exception {DeveloperError} vector must be normalized.
  30. *
  31. * @see AttributeCompression.octDecodeInRange
  32. */
  33. AttributeCompression.octEncodeInRange = function (vector, rangeMax, result) {
  34. //>>includeStart('debug', pragmas.debug);
  35. Check.defined("vector", vector);
  36. Check.defined("result", result);
  37. var magSquared = Cartesian3.magnitudeSquared(vector);
  38. if (Math.abs(magSquared - 1.0) > CesiumMath.EPSILON6) {
  39. throw new DeveloperError("vector must be normalized.");
  40. }
  41. //>>includeEnd('debug');
  42. result.x =
  43. vector.x / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));
  44. result.y =
  45. vector.y / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));
  46. if (vector.z < 0) {
  47. var x = result.x;
  48. var y = result.y;
  49. result.x = (1.0 - Math.abs(y)) * CesiumMath.signNotZero(x);
  50. result.y = (1.0 - Math.abs(x)) * CesiumMath.signNotZero(y);
  51. }
  52. result.x = CesiumMath.toSNorm(result.x, rangeMax);
  53. result.y = CesiumMath.toSNorm(result.y, rangeMax);
  54. return result;
  55. };
  56. /**
  57. * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding.
  58. *
  59. * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding.
  60. * @param {Cartesian2} result The 2 byte oct-encoded unit length vector.
  61. * @returns {Cartesian2} The 2 byte oct-encoded unit length vector.
  62. *
  63. * @exception {DeveloperError} vector must be normalized.
  64. *
  65. * @see AttributeCompression.octEncodeInRange
  66. * @see AttributeCompression.octDecode
  67. */
  68. AttributeCompression.octEncode = function (vector, result) {
  69. return AttributeCompression.octEncodeInRange(vector, 255, result);
  70. };
  71. var octEncodeScratch = new Cartesian2();
  72. var uint8ForceArray = new Uint8Array(1);
  73. function forceUint8(value) {
  74. uint8ForceArray[0] = value;
  75. return uint8ForceArray[0];
  76. }
  77. /**
  78. * @param {Cartesian3} vector The normalized vector to be compressed into 4 byte 'oct' encoding.
  79. * @param {Cartesian4} result The 4 byte oct-encoded unit length vector.
  80. * @returns {Cartesian4} The 4 byte oct-encoded unit length vector.
  81. *
  82. * @exception {DeveloperError} vector must be normalized.
  83. *
  84. * @see AttributeCompression.octEncodeInRange
  85. * @see AttributeCompression.octDecodeFromCartesian4
  86. */
  87. AttributeCompression.octEncodeToCartesian4 = function (vector, result) {
  88. AttributeCompression.octEncodeInRange(vector, 65535, octEncodeScratch);
  89. result.x = forceUint8(octEncodeScratch.x * RIGHT_SHIFT);
  90. result.y = forceUint8(octEncodeScratch.x);
  91. result.z = forceUint8(octEncodeScratch.y * RIGHT_SHIFT);
  92. result.w = forceUint8(octEncodeScratch.y);
  93. return result;
  94. };
  95. /**
  96. * Decodes a unit-length vector in 'oct' encoding to a normalized 3-component vector.
  97. *
  98. * @param {Number} x The x component of the oct-encoded unit length vector.
  99. * @param {Number} y The y component of the oct-encoded unit length vector.
  100. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits.
  101. * @param {Cartesian3} result The decoded and normalized vector
  102. * @returns {Cartesian3} The decoded and normalized vector.
  103. *
  104. * @exception {DeveloperError} x and y must be unsigned normalized integers between 0 and rangeMax.
  105. *
  106. * @see AttributeCompression.octEncodeInRange
  107. */
  108. AttributeCompression.octDecodeInRange = function (x, y, rangeMax, result) {
  109. //>>includeStart('debug', pragmas.debug);
  110. Check.defined("result", result);
  111. if (x < 0 || x > rangeMax || y < 0 || y > rangeMax) {
  112. throw new DeveloperError(
  113. "x and y must be unsigned normalized integers between 0 and " + rangeMax
  114. );
  115. }
  116. //>>includeEnd('debug');
  117. result.x = CesiumMath.fromSNorm(x, rangeMax);
  118. result.y = CesiumMath.fromSNorm(y, rangeMax);
  119. result.z = 1.0 - (Math.abs(result.x) + Math.abs(result.y));
  120. if (result.z < 0.0) {
  121. var oldVX = result.x;
  122. result.x = (1.0 - Math.abs(result.y)) * CesiumMath.signNotZero(oldVX);
  123. result.y = (1.0 - Math.abs(oldVX)) * CesiumMath.signNotZero(result.y);
  124. }
  125. return Cartesian3.normalize(result, result);
  126. };
  127. /**
  128. * Decodes a unit-length vector in 2 byte 'oct' encoding to a normalized 3-component vector.
  129. *
  130. * @param {Number} x The x component of the oct-encoded unit length vector.
  131. * @param {Number} y The y component of the oct-encoded unit length vector.
  132. * @param {Cartesian3} result The decoded and normalized vector.
  133. * @returns {Cartesian3} The decoded and normalized vector.
  134. *
  135. * @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and 255.
  136. *
  137. * @see AttributeCompression.octDecodeInRange
  138. */
  139. AttributeCompression.octDecode = function (x, y, result) {
  140. return AttributeCompression.octDecodeInRange(x, y, 255, result);
  141. };
  142. /**
  143. * Decodes a unit-length vector in 4 byte 'oct' encoding to a normalized 3-component vector.
  144. *
  145. * @param {Cartesian4} encoded The oct-encoded unit length vector.
  146. * @param {Cartesian3} result The decoded and normalized vector.
  147. * @returns {Cartesian3} The decoded and normalized vector.
  148. *
  149. * @exception {DeveloperError} x, y, z, and w must be unsigned normalized integers between 0 and 255.
  150. *
  151. * @see AttributeCompression.octDecodeInRange
  152. * @see AttributeCompression.octEncodeToCartesian4
  153. */
  154. AttributeCompression.octDecodeFromCartesian4 = function (encoded, result) {
  155. //>>includeStart('debug', pragmas.debug);
  156. Check.typeOf.object("encoded", encoded);
  157. Check.typeOf.object("result", result);
  158. //>>includeEnd('debug');
  159. var x = encoded.x;
  160. var y = encoded.y;
  161. var z = encoded.z;
  162. var w = encoded.w;
  163. //>>includeStart('debug', pragmas.debug);
  164. if (
  165. x < 0 ||
  166. x > 255 ||
  167. y < 0 ||
  168. y > 255 ||
  169. z < 0 ||
  170. z > 255 ||
  171. w < 0 ||
  172. w > 255
  173. ) {
  174. throw new DeveloperError(
  175. "x, y, z, and w must be unsigned normalized integers between 0 and 255"
  176. );
  177. }
  178. //>>includeEnd('debug');
  179. var xOct16 = x * LEFT_SHIFT + y;
  180. var yOct16 = z * LEFT_SHIFT + w;
  181. return AttributeCompression.octDecodeInRange(xOct16, yOct16, 65535, result);
  182. };
  183. /**
  184. * Packs an oct encoded vector into a single floating-point number.
  185. *
  186. * @param {Cartesian2} encoded The oct encoded vector.
  187. * @returns {Number} The oct encoded vector packed into a single float.
  188. *
  189. */
  190. AttributeCompression.octPackFloat = function (encoded) {
  191. //>>includeStart('debug', pragmas.debug);
  192. Check.defined("encoded", encoded);
  193. //>>includeEnd('debug');
  194. return 256.0 * encoded.x + encoded.y;
  195. };
  196. var scratchEncodeCart2 = new Cartesian2();
  197. /**
  198. * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding and
  199. * stores those values in a single float-point number.
  200. *
  201. * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding.
  202. * @returns {Number} The 2 byte oct-encoded unit length vector.
  203. *
  204. * @exception {DeveloperError} vector must be normalized.
  205. */
  206. AttributeCompression.octEncodeFloat = function (vector) {
  207. AttributeCompression.octEncode(vector, scratchEncodeCart2);
  208. return AttributeCompression.octPackFloat(scratchEncodeCart2);
  209. };
  210. /**
  211. * Decodes a unit-length vector in 'oct' encoding packed in a floating-point number to a normalized 3-component vector.
  212. *
  213. * @param {Number} value The oct-encoded unit length vector stored as a single floating-point number.
  214. * @param {Cartesian3} result The decoded and normalized vector
  215. * @returns {Cartesian3} The decoded and normalized vector.
  216. *
  217. */
  218. AttributeCompression.octDecodeFloat = function (value, result) {
  219. //>>includeStart('debug', pragmas.debug);
  220. Check.defined("value", value);
  221. //>>includeEnd('debug');
  222. var temp = value / 256.0;
  223. var x = Math.floor(temp);
  224. var y = (temp - x) * 256.0;
  225. return AttributeCompression.octDecode(x, y, result);
  226. };
  227. /**
  228. * Encodes three normalized vectors into 6 SNORM values in the range of [0-255] following the 'oct' encoding and
  229. * packs those into two floating-point numbers.
  230. *
  231. * @param {Cartesian3} v1 A normalized vector to be compressed.
  232. * @param {Cartesian3} v2 A normalized vector to be compressed.
  233. * @param {Cartesian3} v3 A normalized vector to be compressed.
  234. * @param {Cartesian2} result The 'oct' encoded vectors packed into two floating-point numbers.
  235. * @returns {Cartesian2} The 'oct' encoded vectors packed into two floating-point numbers.
  236. *
  237. */
  238. AttributeCompression.octPack = function (v1, v2, v3, result) {
  239. //>>includeStart('debug', pragmas.debug);
  240. Check.defined("v1", v1);
  241. Check.defined("v2", v2);
  242. Check.defined("v3", v3);
  243. Check.defined("result", result);
  244. //>>includeEnd('debug');
  245. var encoded1 = AttributeCompression.octEncodeFloat(v1);
  246. var encoded2 = AttributeCompression.octEncodeFloat(v2);
  247. var encoded3 = AttributeCompression.octEncode(v3, scratchEncodeCart2);
  248. result.x = 65536.0 * encoded3.x + encoded1;
  249. result.y = 65536.0 * encoded3.y + encoded2;
  250. return result;
  251. };
  252. /**
  253. * Decodes three unit-length vectors in 'oct' encoding packed into a floating-point number to a normalized 3-component vector.
  254. *
  255. * @param {Cartesian2} packed The three oct-encoded unit length vectors stored as two floating-point number.
  256. * @param {Cartesian3} v1 One decoded and normalized vector.
  257. * @param {Cartesian3} v2 One decoded and normalized vector.
  258. * @param {Cartesian3} v3 One decoded and normalized vector.
  259. */
  260. AttributeCompression.octUnpack = function (packed, v1, v2, v3) {
  261. //>>includeStart('debug', pragmas.debug);
  262. Check.defined("packed", packed);
  263. Check.defined("v1", v1);
  264. Check.defined("v2", v2);
  265. Check.defined("v3", v3);
  266. //>>includeEnd('debug');
  267. var temp = packed.x / 65536.0;
  268. var x = Math.floor(temp);
  269. var encodedFloat1 = (temp - x) * 65536.0;
  270. temp = packed.y / 65536.0;
  271. var y = Math.floor(temp);
  272. var encodedFloat2 = (temp - y) * 65536.0;
  273. AttributeCompression.octDecodeFloat(encodedFloat1, v1);
  274. AttributeCompression.octDecodeFloat(encodedFloat2, v2);
  275. AttributeCompression.octDecode(x, y, v3);
  276. };
  277. /**
  278. * Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision.
  279. *
  280. * @param {Cartesian2} textureCoordinates The texture coordinates to compress. Both coordinates must be in the range 0.0-1.0.
  281. * @returns {Number} The packed texture coordinates.
  282. *
  283. */
  284. AttributeCompression.compressTextureCoordinates = function (
  285. textureCoordinates
  286. ) {
  287. //>>includeStart('debug', pragmas.debug);
  288. Check.defined("textureCoordinates", textureCoordinates);
  289. //>>includeEnd('debug');
  290. // Move x and y to the range 0-4095;
  291. var x = (textureCoordinates.x * 4095.0) | 0;
  292. var y = (textureCoordinates.y * 4095.0) | 0;
  293. return 4096.0 * x + y;
  294. };
  295. /**
  296. * Decompresses texture coordinates that were packed into a single float.
  297. *
  298. * @param {Number} compressed The compressed texture coordinates.
  299. * @param {Cartesian2} result The decompressed texture coordinates.
  300. * @returns {Cartesian2} The modified result parameter.
  301. *
  302. */
  303. AttributeCompression.decompressTextureCoordinates = function (
  304. compressed,
  305. result
  306. ) {
  307. //>>includeStart('debug', pragmas.debug);
  308. Check.defined("compressed", compressed);
  309. Check.defined("result", result);
  310. //>>includeEnd('debug');
  311. var temp = compressed / 4096.0;
  312. var xZeroTo4095 = Math.floor(temp);
  313. result.x = xZeroTo4095 / 4095.0;
  314. result.y = (compressed - xZeroTo4095 * 4096) / 4095;
  315. return result;
  316. };
  317. function zigZagDecode(value) {
  318. return (value >> 1) ^ -(value & 1);
  319. }
  320. /**
  321. * Decodes delta and ZigZag encoded vertices. This modifies the buffers in place.
  322. *
  323. * @param {Uint16Array} uBuffer The buffer view of u values.
  324. * @param {Uint16Array} vBuffer The buffer view of v values.
  325. * @param {Uint16Array} [heightBuffer] The buffer view of height values.
  326. *
  327. * @see {@link https://github.com/CesiumGS/quantized-mesh|quantized-mesh-1.0 terrain format}
  328. */
  329. AttributeCompression.zigZagDeltaDecode = function (
  330. uBuffer,
  331. vBuffer,
  332. heightBuffer
  333. ) {
  334. //>>includeStart('debug', pragmas.debug);
  335. Check.defined("uBuffer", uBuffer);
  336. Check.defined("vBuffer", vBuffer);
  337. Check.typeOf.number.equals(
  338. "uBuffer.length",
  339. "vBuffer.length",
  340. uBuffer.length,
  341. vBuffer.length
  342. );
  343. if (defined(heightBuffer)) {
  344. Check.typeOf.number.equals(
  345. "uBuffer.length",
  346. "heightBuffer.length",
  347. uBuffer.length,
  348. heightBuffer.length
  349. );
  350. }
  351. //>>includeEnd('debug');
  352. var count = uBuffer.length;
  353. var u = 0;
  354. var v = 0;
  355. var height = 0;
  356. for (var i = 0; i < count; ++i) {
  357. u += zigZagDecode(uBuffer[i]);
  358. v += zigZagDecode(vBuffer[i]);
  359. uBuffer[i] = u;
  360. vBuffer[i] = v;
  361. if (defined(heightBuffer)) {
  362. height += zigZagDecode(heightBuffer[i]);
  363. heightBuffer[i] = height;
  364. }
  365. }
  366. };
  367. export default AttributeCompression;