import Check from "../Core/Check.js"; import DeveloperError from "../Core/DeveloperError.js"; import defined from "../Core/defined.js"; import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js"; import MetadataTable from "./MetadataTable.js"; /** * An object representing voxel content for a {@link Cesium3DTilesVoxelProvider}. * * @alias VoxelContent * @constructor * * @param {Resource} resource The resource for this voxel content. This is used for fetching external buffers as needed. * * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function VoxelContent(resource) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("resource", resource); //>>includeEnd('debug'); this._resource = resource; this._metadataTable = undefined; } Object.defineProperties(VoxelContent.prototype, { /** * The {@link MetadataTable} storing voxel property values. * * @type {MetadataTable} * @readonly * @private */ metadataTable: { get: function () { return this._metadataTable; }, }, }); /** * Creates an object representing voxel content for a {@link Cesium3DTilesVoxelProvider}. * * @param {Resource} resource The resource for this voxel content. This is used for fetching external buffers as needed. * @param {object} [json] Voxel JSON contents. Mutually exclusive with binary. * @param {Uint8Array} [binary] Voxel binary contents. Mutually exclusive with json. * @param {MetadataSchema} metadataSchema The metadata schema used by property tables in the voxel content * @returns {Promise} * * @exception {DeveloperError} One of json and binary must be defined. */ VoxelContent.fromJson = async function ( resource, json, binary, metadataSchema ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("resource", resource); if (defined(json) === defined(binary)) { throw new DeveloperError("One of json and binary must be defined."); } //>>includeEnd('debug'); let chunks; if (defined(json)) { chunks = { json: json, binary: undefined, }; } else { chunks = parseVoxelChunks(binary); } const buffersU8 = await requestBuffers(resource, chunks.json, chunks.binary); const bufferViewsU8 = {}; const bufferViewsLength = chunks.json.bufferViews.length; for (let i = 0; i < bufferViewsLength; ++i) { const bufferViewJson = chunks.json.bufferViews[i]; const start = bufferViewJson.byteOffset; const end = start + bufferViewJson.byteLength; const buffer = buffersU8[bufferViewJson.buffer]; const bufferView = buffer.subarray(start, end); bufferViewsU8[i] = bufferView; } const propertyTableIndex = chunks.json.voxelTable; const propertyTableJson = chunks.json.propertyTables[propertyTableIndex]; const content = new VoxelContent(resource); content._metadataTable = new MetadataTable({ count: propertyTableJson.count, properties: propertyTableJson.properties, class: metadataSchema.classes[propertyTableJson.class], bufferViews: bufferViewsU8, }); return content; }; function requestBuffers(resource, json, binary) { const buffersLength = json.buffers.length; const bufferPromises = new Array(buffersLength); for (let i = 0; i < buffersLength; i++) { const buffer = json.buffers[i]; if (defined(buffer.uri)) { const baseResource = resource; const bufferResource = baseResource.getDerivedResource({ url: buffer.uri, }); bufferPromises[i] = bufferResource .fetchArrayBuffer() .then(function (arrayBuffer) { return new Uint8Array(arrayBuffer); }); } else { bufferPromises[i] = Promise.resolve(binary); } } return Promise.all(bufferPromises); } /** * A helper object for storing the two parts of the binary voxel content * * @typedef {object} VoxelChunks * @property {object} json The json chunk of the binary voxel content * @property {Uint8Array} binary The binary chunk of the binary voxel content. This represents the internal buffer. * @private */ /** * Given binary voxel content, split into JSON and binary chunks * * @param {Uint8Array} binaryView The binary voxel content * @returns {VoxelChunks} An object containing the JSON and binary chunks * @private */ function parseVoxelChunks(binaryView) { // Parse the header const littleEndian = true; const reader = new DataView(binaryView.buffer, binaryView.byteOffset); // Skip to the chunk lengths let byteOffset = 8; // Read the bottom 32 bits of the 64-bit byte length. This is ok for now because: // 1) not all browsers have native 64-bit operations // 2) the data is well under 4GB const jsonByteLength = reader.getUint32(byteOffset, littleEndian); byteOffset += 8; const binaryByteLength = reader.getUint32(byteOffset, littleEndian); byteOffset += 8; const json = getJsonFromTypedArray(binaryView, byteOffset, jsonByteLength); byteOffset += jsonByteLength; const binary = binaryView.subarray(byteOffset, byteOffset + binaryByteLength); return { json: json, binary: binary, }; } export default VoxelContent;