AttributeCompression.js 15 KB

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