| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 | import Cartesian2 from "./Cartesian2.js";import Cartesian3 from "./Cartesian3.js";import ComponentDatatype from "./ComponentDatatype.js";import Check from "./Check.js";import defined from "./defined.js";import DeveloperError from "./DeveloperError.js";import CesiumMath from "./Math.js";import AttributeType from "../Scene/AttributeType.js";const RIGHT_SHIFT = 1.0 / 256.0;const LEFT_SHIFT = 256.0;/** * Attribute compression and decompression functions. * * @namespace AttributeCompression * * @private */const AttributeCompression = {};/** * Encodes a normalized vector into 2 SNORM values in the range of [0-rangeMax] following the 'oct' encoding. * * Oct encoding is a compact representation of unit length vectors. * The 'oct' encoding is described in "A Survey of Efficient Representations of Independent Unit Vectors", * Cigolle et al 2014: {@link http://jcgt.org/published/0003/02/01/} * * @param {Cartesian3} vector The normalized vector to be compressed into 2 component 'oct' encoding. * @param {Cartesian2} result The 2 component oct-encoded unit length vector. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits. * @returns {Cartesian2} The 2 component oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octDecodeInRange */AttributeCompression.octEncodeInRange = function (vector, rangeMax, result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("vector", vector);  Check.defined("result", result);  const magSquared = Cartesian3.magnitudeSquared(vector);  if (Math.abs(magSquared - 1.0) > CesiumMath.EPSILON6) {    throw new DeveloperError("vector must be normalized.");  }  //>>includeEnd('debug');  result.x =    vector.x / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));  result.y =    vector.y / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z));  if (vector.z < 0) {    const x = result.x;    const y = result.y;    result.x = (1.0 - Math.abs(y)) * CesiumMath.signNotZero(x);    result.y = (1.0 - Math.abs(x)) * CesiumMath.signNotZero(y);  }  result.x = CesiumMath.toSNorm(result.x, rangeMax);  result.y = CesiumMath.toSNorm(result.y, rangeMax);  return result;};/** * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding. * * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding. * @param {Cartesian2} result The 2 byte oct-encoded unit length vector. * @returns {Cartesian2} The 2 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octEncodeInRange * @see AttributeCompression.octDecode */AttributeCompression.octEncode = function (vector, result) {  return AttributeCompression.octEncodeInRange(vector, 255, result);};const octEncodeScratch = new Cartesian2();const uint8ForceArray = new Uint8Array(1);function forceUint8(value) {  uint8ForceArray[0] = value;  return uint8ForceArray[0];}/** * @param {Cartesian3} vector The normalized vector to be compressed into 4 byte 'oct' encoding. * @param {Cartesian4} result The 4 byte oct-encoded unit length vector. * @returns {Cartesian4} The 4 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octEncodeInRange * @see AttributeCompression.octDecodeFromCartesian4 */AttributeCompression.octEncodeToCartesian4 = function (vector, result) {  AttributeCompression.octEncodeInRange(vector, 65535, octEncodeScratch);  result.x = forceUint8(octEncodeScratch.x * RIGHT_SHIFT);  result.y = forceUint8(octEncodeScratch.x);  result.z = forceUint8(octEncodeScratch.y * RIGHT_SHIFT);  result.w = forceUint8(octEncodeScratch.y);  return result;};/** * Decodes a unit-length vector in 'oct' encoding to a normalized 3-component vector. * * @param {Number} x The x component of the oct-encoded unit length vector. * @param {Number} y The y component of the oct-encoded unit length vector. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits. * @param {Cartesian3} result The decoded and normalized vector * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x and y must be unsigned normalized integers between 0 and rangeMax. * * @see AttributeCompression.octEncodeInRange */AttributeCompression.octDecodeInRange = function (x, y, rangeMax, result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("result", result);  if (x < 0 || x > rangeMax || y < 0 || y > rangeMax) {    throw new DeveloperError(      `x and y must be unsigned normalized integers between 0 and ${rangeMax}`    );  }  //>>includeEnd('debug');  result.x = CesiumMath.fromSNorm(x, rangeMax);  result.y = CesiumMath.fromSNorm(y, rangeMax);  result.z = 1.0 - (Math.abs(result.x) + Math.abs(result.y));  if (result.z < 0.0) {    const oldVX = result.x;    result.x = (1.0 - Math.abs(result.y)) * CesiumMath.signNotZero(oldVX);    result.y = (1.0 - Math.abs(oldVX)) * CesiumMath.signNotZero(result.y);  }  return Cartesian3.normalize(result, result);};/** * Decodes a unit-length vector in 2 byte 'oct' encoding to a normalized 3-component vector. * * @param {Number} x The x component of the oct-encoded unit length vector. * @param {Number} y The y component of the oct-encoded unit length vector. * @param {Cartesian3} result The decoded and normalized vector. * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and 255. * * @see AttributeCompression.octDecodeInRange */AttributeCompression.octDecode = function (x, y, result) {  return AttributeCompression.octDecodeInRange(x, y, 255, result);};/** * Decodes a unit-length vector in 4 byte 'oct' encoding to a normalized 3-component vector. * * @param {Cartesian4} encoded The oct-encoded unit length vector. * @param {Cartesian3} result The decoded and normalized vector. * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x, y, z, and w must be unsigned normalized integers between 0 and 255. * * @see AttributeCompression.octDecodeInRange * @see AttributeCompression.octEncodeToCartesian4 */AttributeCompression.octDecodeFromCartesian4 = function (encoded, result) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("encoded", encoded);  Check.typeOf.object("result", result);  //>>includeEnd('debug');  const x = encoded.x;  const y = encoded.y;  const z = encoded.z;  const w = encoded.w;  //>>includeStart('debug', pragmas.debug);  if (    x < 0 ||    x > 255 ||    y < 0 ||    y > 255 ||    z < 0 ||    z > 255 ||    w < 0 ||    w > 255  ) {    throw new DeveloperError(      "x, y, z, and w must be unsigned normalized integers between 0 and 255"    );  }  //>>includeEnd('debug');  const xOct16 = x * LEFT_SHIFT + y;  const yOct16 = z * LEFT_SHIFT + w;  return AttributeCompression.octDecodeInRange(xOct16, yOct16, 65535, result);};/** * Packs an oct encoded vector into a single floating-point number. * * @param {Cartesian2} encoded The oct encoded vector. * @returns {Number} The oct encoded vector packed into a single float. * */AttributeCompression.octPackFloat = function (encoded) {  //>>includeStart('debug', pragmas.debug);  Check.defined("encoded", encoded);  //>>includeEnd('debug');  return 256.0 * encoded.x + encoded.y;};const scratchEncodeCart2 = new Cartesian2();/** * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding and * stores those values in a single float-point number. * * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding. * @returns {Number} The 2 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. */AttributeCompression.octEncodeFloat = function (vector) {  AttributeCompression.octEncode(vector, scratchEncodeCart2);  return AttributeCompression.octPackFloat(scratchEncodeCart2);};/** * Decodes a unit-length vector in 'oct' encoding packed in a floating-point number to a normalized 3-component vector. * * @param {Number} value The oct-encoded unit length vector stored as a single floating-point number. * @param {Cartesian3} result The decoded and normalized vector * @returns {Cartesian3} The decoded and normalized vector. * */AttributeCompression.octDecodeFloat = function (value, result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("value", value);  //>>includeEnd('debug');  const temp = value / 256.0;  const x = Math.floor(temp);  const y = (temp - x) * 256.0;  return AttributeCompression.octDecode(x, y, result);};/** * Encodes three normalized vectors into 6 SNORM values in the range of [0-255] following the 'oct' encoding and * packs those into two floating-point numbers. * * @param {Cartesian3} v1 A normalized vector to be compressed. * @param {Cartesian3} v2 A normalized vector to be compressed. * @param {Cartesian3} v3 A normalized vector to be compressed. * @param {Cartesian2} result The 'oct' encoded vectors packed into two floating-point numbers. * @returns {Cartesian2} The 'oct' encoded vectors packed into two floating-point numbers. * */AttributeCompression.octPack = function (v1, v2, v3, result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("v1", v1);  Check.defined("v2", v2);  Check.defined("v3", v3);  Check.defined("result", result);  //>>includeEnd('debug');  const encoded1 = AttributeCompression.octEncodeFloat(v1);  const encoded2 = AttributeCompression.octEncodeFloat(v2);  const encoded3 = AttributeCompression.octEncode(v3, scratchEncodeCart2);  result.x = 65536.0 * encoded3.x + encoded1;  result.y = 65536.0 * encoded3.y + encoded2;  return result;};/** * Decodes three unit-length vectors in 'oct' encoding packed into a floating-point number to a normalized 3-component vector. * * @param {Cartesian2} packed The three oct-encoded unit length vectors stored as two floating-point number. * @param {Cartesian3} v1 One decoded and normalized vector. * @param {Cartesian3} v2 One decoded and normalized vector. * @param {Cartesian3} v3 One decoded and normalized vector. */AttributeCompression.octUnpack = function (packed, v1, v2, v3) {  //>>includeStart('debug', pragmas.debug);  Check.defined("packed", packed);  Check.defined("v1", v1);  Check.defined("v2", v2);  Check.defined("v3", v3);  //>>includeEnd('debug');  let temp = packed.x / 65536.0;  const x = Math.floor(temp);  const encodedFloat1 = (temp - x) * 65536.0;  temp = packed.y / 65536.0;  const y = Math.floor(temp);  const encodedFloat2 = (temp - y) * 65536.0;  AttributeCompression.octDecodeFloat(encodedFloat1, v1);  AttributeCompression.octDecodeFloat(encodedFloat2, v2);  AttributeCompression.octDecode(x, y, v3);};/** * Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision. * * @param {Cartesian2} textureCoordinates The texture coordinates to compress.  Both coordinates must be in the range 0.0-1.0. * @returns {Number} The packed texture coordinates. * */AttributeCompression.compressTextureCoordinates = function (  textureCoordinates) {  //>>includeStart('debug', pragmas.debug);  Check.defined("textureCoordinates", textureCoordinates);  //>>includeEnd('debug');  // Move x and y to the range 0-4095;  const x = (textureCoordinates.x * 4095.0) | 0;  const y = (textureCoordinates.y * 4095.0) | 0;  return 4096.0 * x + y;};/** * Decompresses texture coordinates that were packed into a single float. * * @param {Number} compressed The compressed texture coordinates. * @param {Cartesian2} result The decompressed texture coordinates. * @returns {Cartesian2} The modified result parameter. * */AttributeCompression.decompressTextureCoordinates = function (  compressed,  result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("compressed", compressed);  Check.defined("result", result);  //>>includeEnd('debug');  const temp = compressed / 4096.0;  const xZeroTo4095 = Math.floor(temp);  result.x = xZeroTo4095 / 4095.0;  result.y = (compressed - xZeroTo4095 * 4096) / 4095;  return result;};function zigZagDecode(value) {  return (value >> 1) ^ -(value & 1);}/** * Decodes delta and ZigZag encoded vertices. This modifies the buffers in place. * * @param {Uint16Array} uBuffer The buffer view of u values. * @param {Uint16Array} vBuffer The buffer view of v values. * @param {Uint16Array} [heightBuffer] The buffer view of height values. * * @see {@link https://github.com/CesiumGS/quantized-mesh|quantized-mesh-1.0 terrain format} */AttributeCompression.zigZagDeltaDecode = function (  uBuffer,  vBuffer,  heightBuffer) {  //>>includeStart('debug', pragmas.debug);  Check.defined("uBuffer", uBuffer);  Check.defined("vBuffer", vBuffer);  Check.typeOf.number.equals(    "uBuffer.length",    "vBuffer.length",    uBuffer.length,    vBuffer.length  );  if (defined(heightBuffer)) {    Check.typeOf.number.equals(      "uBuffer.length",      "heightBuffer.length",      uBuffer.length,      heightBuffer.length    );  }  //>>includeEnd('debug');  const count = uBuffer.length;  let u = 0;  let v = 0;  let height = 0;  for (let i = 0; i < count; ++i) {    u += zigZagDecode(uBuffer[i]);    v += zigZagDecode(vBuffer[i]);    uBuffer[i] = u;    vBuffer[i] = v;    if (defined(heightBuffer)) {      height += zigZagDecode(heightBuffer[i]);      heightBuffer[i] = height;    }  }};/** * Dequantizes a quantized typed array into a floating point typed array. * * @see {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data} * * @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array} typedArray The typed array for the quantized data. * @param {ComponentDatatype} componentDatatype The component datatype of the quantized data. * @param {AttributeType} type The attribute type of the quantized data. * @param {Number} count The number of attributes referenced in the dequantized array. * * @returns {Float32Array} The dequantized array. */AttributeCompression.dequantize = function (  typedArray,  componentDatatype,  type,  count) {  //>>includeStart('debug', pragmas.debug);  Check.defined("typedArray", typedArray);  Check.defined("componentDatatype", componentDatatype);  Check.defined("type", type);  Check.defined("count", count);  //>>includeEnd('debug');  const componentsPerAttribute = AttributeType.getNumberOfComponents(type);  let divisor;  switch (componentDatatype) {    case ComponentDatatype.BYTE:      divisor = 127.0;      break;    case ComponentDatatype.UNSIGNED_BYTE:      divisor = 255.0;      break;    case ComponentDatatype.SHORT:      divisor = 32767.0;      break;    case ComponentDatatype.UNSIGNED_SHORT:      divisor = 65535.0;      break;    case ComponentDatatype.INT:      divisor = 2147483647.0;      break;    case ComponentDatatype.UNSIGNED_INT:      divisor = 4294967295.0;      break;    //>>includeStart('debug', pragmas.debug);    default:      throw new DeveloperError(        `Cannot dequantize component datatype: ${componentDatatype}`      );    //>>includeEnd('debug');  }  const dequantizedTypedArray = new Float32Array(    count * componentsPerAttribute  );  for (let i = 0; i < count; i++) {    for (let j = 0; j < componentsPerAttribute; j++) {      const index = i * componentsPerAttribute + j;      dequantizedTypedArray[index] = Math.max(        typedArray[index] / divisor,        -1.0      );    }  }  return dequantizedTypedArray;};/** * Decode RGB565-encoded colors into a floating point typed array containing * normalized RGB values. * * @param {Uint16Array} typedArray Array of RGB565 values * @param {Float32Array} [result] Array to store the normalized VEC3 result */AttributeCompression.decodeRGB565 = function (typedArray, result) {  //>>includeStart('debug', pragmas.debug);  Check.defined("typedArray", typedArray);  const expectedLength = typedArray.length * 3;  if (defined(result)) {    Check.typeOf.number.equals(      "result.length",      "typedArray.length * 3",      result.length,      expectedLength    );  }  //>>includeEnd('debug');  const count = typedArray.length;  if (!defined(result)) {    result = new Float32Array(count * 3);  }  const mask5 = (1 << 5) - 1;  const mask6 = (1 << 6) - 1;  const normalize5 = 1.0 / 31.0;  const normalize6 = 1.0 / 63.0;  for (let i = 0; i < count; i++) {    const value = typedArray[i];    const red = value >> 11;    const green = (value >> 5) & mask6;    const blue = value & mask5;    const offset = 3 * i;    result[offset] = red * normalize5;    result[offset + 1] = green * normalize6;    result[offset + 2] = blue * normalize5;  }  return result;};export default AttributeCompression;
 |