UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

232 lines (207 loc) 7.46 kB
import { destroyObject } from "@cesium/engine"; import Check from "../Core/Check.js"; import ComponentDatatype from "../Core/ComponentDatatype.js"; import DeveloperError from "../Core/DeveloperError.js"; import defined from "../Core/defined.js"; import GltfLoader from "./GltfLoader.js"; import MetadataComponentType from "./MetadataComponentType.js"; import MetadataType from "./MetadataType.js"; /** * <div class="notice"> * To construct a VoxelContent, call {@link VoxelContent.fromMetadataArray} or {@link VoxelContent.fromGltf}. Do not call the constructor directly. * </div> * An object representing voxel content for a {@link Cesium3DTilesVoxelProvider}. * * @alias VoxelContent * @internalConstructor * * @privateParam {object} options An object with the following properties: * @privateParam {ResourceLoader} [options.loader] The loader used to load the voxel content. * @privateParam {Int8Array[]|Uint8Array[]|Int16Array[]|Uint16Array[]|Int32Array[]|Uint32Array[]|Float32Array[]|Float64Array[]} [options.metadata] The metadata for this voxel content. * * @exception {DeveloperError} One of loader and metadata must be defined. * @exception {DeveloperError} metadata must be an array of TypedArrays. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function VoxelContent(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); if (!defined(options.loader)) { if (!defined(options.metadata)) { throw new DeveloperError("One of loader and metadata must be defined."); } if (!Array.isArray(options.metadata)) { throw new DeveloperError("metadata must be an array of TypedArrays."); } } //>>includeEnd('debug'); const { loader, metadata } = options; this._loader = loader; this._metadata = metadata; this._resourcesLoaded = false; this._ready = false; } Object.defineProperties(VoxelContent.prototype, { /** * Returns true when the content is ready to render; otherwise false * * @memberof VoxelContent.prototype * * @type {boolean} * @readonly * @private */ ready: { get: function () { return this._ready; }, }, /** * The metadata for this voxel content. * The metadata is an array of typed arrays, one for each field. * The data for one field is a flattened 3D array ordered by X, then Y, then Z. * @type {Int8Array[]|Uint8Array[]|Int16Array[]|Uint16Array[]|Int32Array[]|Uint32Array[]|Float32Array[]|Float64Array[]} * @readonly */ metadata: { get: function () { return this._metadata; }, }, }); /** * Constructs a VoxelContent from an array of metadata. * * @param {Int8Array[]|Uint8Array[]|Int16Array[]|Uint16Array[]|Int32Array[]|Uint32Array[]|Float32Array[]|Float64Array[]} metadata The metadata to use for this voxel content. * @returns {VoxelContent} A VoxelContent containing the specified metadata. */ VoxelContent.fromMetadataArray = function (metadata) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("metadata", metadata); if (!Array.isArray(metadata)) { throw new DeveloperError("metadata must be an array of TypedArrays."); } //>>includeEnd('debug'); return new VoxelContent({ metadata }); }; /** * Constructs a VoxelContent from a glTF resource. * * @param {Resource} resource A Resource pointing to a glTF containing voxel content. * @returns {Promise<VoxelContent>} A promise that resolves to the voxel content. * * @private */ VoxelContent.fromGltf = async function (resource) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("resource", resource); //>>includeEnd('debug'); // Construct the glTF loader const loader = new GltfLoader({ gltfResource: resource, releaseGltfJson: false, loadAttributesAsTypedArray: true, }); try { // This loads the gltf JSON and ensures the gltf is valid // Further resource loading is handled synchronously in loader.process() // via voxelContent.update() as the frameState is needed await loader.load(); } catch (error) { loader.destroy(); throw error; } return new VoxelContent({ loader }); }; /** * Updates the content until all resources are ready for rendering. * @param {FrameState} frameState The frame state * @private */ VoxelContent.prototype.update = function (primitive, frameState) { const loader = this._loader; if (this._ready) { // Nothing to do return; } // Ensures frames continue to render in requestRender mode while resources are processing frameState.afterRender.push(() => true); if (!defined(loader)) { this._ready = true; return; } if (this._resourcesLoaded) { const { structuralMetadata, scene } = loader.components; const { attributes } = scene.nodes[0].primitives[0]; this._metadata = processAttributes( attributes, structuralMetadata, primitive, ); this._ready = true; return; } this._resourcesLoaded = loader.process(frameState); }; /** * Processes the attributes from the glTF loader, reordering them into the order expected by the primitive. * * @param {ModelComponents.Attribute[]} attributes The attributes to process * @param {StructuralMetadata} structuralMetadata Information from the glTF EXT_structural_metadata extension * @param {VoxelPrimitive} primitive The primitive for which this voxel content will be used. * @returns {Int8Array[]|Uint8Array[]|Int16Array[]|Uint16Array[]|Int32Array[]|Uint32Array[]|Float32Array[]|Float64Array[]} An array of typed arrays containing the attribute values * @private */ function processAttributes(attributes, structuralMetadata, primitive) { const { className, names, types, componentTypes } = primitive.provider; const propertyAttribute = structuralMetadata.propertyAttributes.find( (p) => p.class.id === className, ); const { properties } = propertyAttribute; const data = new Array(names.length); for (let i = 0; i < attributes.length; i++) { // Find the appropriate glTF attribute based on its name. const name = properties[names[i]].attribute; const attribute = attributes.find((a) => a.name === name); if (!defined(attribute)) { continue; } const componentDatatype = MetadataComponentType.toComponentDatatype( componentTypes[i], ); const componentCount = MetadataType.getComponentCount(types[i]); const totalCount = attribute.count * componentCount; data[i] = ComponentDatatype.createArrayBufferView( componentDatatype, attribute.typedArray.buffer, attribute.typedArray.byteOffset + attribute.byteOffset, totalCount, ); } return data; } /** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see VoxelContent#destroy * * @private */ VoxelContent.prototype.isDestroyed = function () { return false; }; /** * Frees the resources used by this object. * @private */ VoxelContent.prototype.destroy = function () { this._loader = this._loader && this._loader.destroy(); return destroyObject(this); }; export default VoxelContent;