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 */ var B3dmParser = {}; B3dmParser._deprecationWarning = deprecationWarning; var 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) { var byteStart = defaultValue(byteOffset, 0); //>>includeStart('debug', pragmas.debug); Check.defined("arrayBuffer", arrayBuffer); //>>includeEnd('debug'); byteOffset = byteStart; var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32; // Skip magic var 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; var byteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var featureTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var 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." ); } var featureTableJson; if (featureTableJsonByteLength === 0) { featureTableJson = { BATCH_LENGTH: defaultValue(batchLength, 0), }; } else { featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJsonByteLength ); byteOffset += featureTableJsonByteLength; } var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; var batchTableJson; var 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; } } var gltfByteLength = byteStart + byteLength - byteOffset; if (gltfByteLength === 0) { throw new RuntimeError("glTF byte length must be greater than 0."); } var 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;