| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 | import Check from "../Core/Check.js";import defaultValue from "../Core/defaultValue.js";import deprecationWarning from "../Core/deprecationWarning.js";import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";import RuntimeError from "../Core/RuntimeError.js";/** * Handles parsing of a Batched 3D Model. * * @namespace B3dmParser * @private */const B3dmParser = {};B3dmParser._deprecationWarning = deprecationWarning;const sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;/** * Parses the contents of a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel|Batched 3D Model}. * * @private * * @param {ArrayBuffer} arrayBuffer The array buffer containing the b3dm. * @param {Number} [byteOffset=0] The byte offset of the beginning of the b3dm in the array buffer. * @returns {Object} Returns an object with the batch length, feature table (binary and json), batch table (binary and json) and glTF parts of the b3dm. */B3dmParser.parse = function (arrayBuffer, byteOffset) {  const byteStart = defaultValue(byteOffset, 0);  //>>includeStart('debug', pragmas.debug);  Check.defined("arrayBuffer", arrayBuffer);  //>>includeEnd('debug');  byteOffset = byteStart;  const uint8Array = new Uint8Array(arrayBuffer);  const view = new DataView(arrayBuffer);  byteOffset += sizeOfUint32; // Skip magic  const version = view.getUint32(byteOffset, true);  if (version !== 1) {    throw new RuntimeError(      `Only Batched 3D Model version 1 is supported.  Version ${version} is not.`    );  }  byteOffset += sizeOfUint32;  const byteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  let featureTableJsonByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  let featureTableBinaryByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  let batchTableJsonByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  let batchTableBinaryByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  let batchLength;  // Legacy header #1: [batchLength] [batchTableByteLength]  // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]  // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength]  // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic.  // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length.  // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead  if (batchTableJsonByteLength >= 570425344) {    // First legacy check    byteOffset -= sizeOfUint32 * 2;    batchLength = featureTableJsonByteLength;    batchTableJsonByteLength = featureTableBinaryByteLength;    batchTableBinaryByteLength = 0;    featureTableJsonByteLength = 0;    featureTableBinaryByteLength = 0;    B3dmParser._deprecationWarning(      "b3dm-legacy-header",      "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel."    );  } else if (batchTableBinaryByteLength >= 570425344) {    // Second legacy check    byteOffset -= sizeOfUint32;    batchLength = batchTableJsonByteLength;    batchTableJsonByteLength = featureTableJsonByteLength;    batchTableBinaryByteLength = featureTableBinaryByteLength;    featureTableJsonByteLength = 0;    featureTableBinaryByteLength = 0;    B3dmParser._deprecationWarning(      "b3dm-legacy-header",      "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel."    );  }  let featureTableJson;  if (featureTableJsonByteLength === 0) {    featureTableJson = {      BATCH_LENGTH: defaultValue(batchLength, 0),    };  } else {    featureTableJson = getJsonFromTypedArray(      uint8Array,      byteOffset,      featureTableJsonByteLength    );    byteOffset += featureTableJsonByteLength;  }  const featureTableBinary = new Uint8Array(    arrayBuffer,    byteOffset,    featureTableBinaryByteLength  );  byteOffset += featureTableBinaryByteLength;  let batchTableJson;  let batchTableBinary;  if (batchTableJsonByteLength > 0) {    // PERFORMANCE_IDEA: is it possible to allocate this on-demand?  Perhaps keep the    // arraybuffer/string compressed in memory and then decompress it when it is first accessed.    //    // We could also make another request for it, but that would make the property set/get    // API async, and would double the number of numbers in some cases.    batchTableJson = getJsonFromTypedArray(      uint8Array,      byteOffset,      batchTableJsonByteLength    );    byteOffset += batchTableJsonByteLength;    if (batchTableBinaryByteLength > 0) {      // Has a batch table binary      batchTableBinary = new Uint8Array(        arrayBuffer,        byteOffset,        batchTableBinaryByteLength      );      // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed      batchTableBinary = new Uint8Array(batchTableBinary);      byteOffset += batchTableBinaryByteLength;    }  }  const gltfByteLength = byteStart + byteLength - byteOffset;  if (gltfByteLength === 0) {    throw new RuntimeError("glTF byte length must be greater than 0.");  }  let gltfView;  if (byteOffset % 4 === 0) {    gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);  } else {    // Create a copy of the glb so that it is 4-byte aligned    B3dmParser._deprecationWarning(      "b3dm-glb-unaligned",      "The embedded glb is not aligned to a 4-byte boundary."    );    gltfView = new Uint8Array(      uint8Array.subarray(byteOffset, byteOffset + gltfByteLength)    );  }  return {    batchLength: batchLength,    featureTableJson: featureTableJson,    featureTableBinary: featureTableBinary,    batchTableJson: batchTableJson,    batchTableBinary: batchTableBinary,    gltf: gltfView,  };};export default B3dmParser;
 |