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;
|