@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
174 lines (152 loc) • 6.65 kB
JavaScript
import Check from "../Core/Check.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 = 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: 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;