UNPKG

@loaders.gl/gltf

Version:

Framework-independent loader for the glTF format

1,429 lines (1,418 loc) 141 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name12 in all) __defProp(target, name12, { get: all[name12], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/index.js var dist_exports = {}; __export(dist_exports, { EXT_FEATURE_METADATA: () => name3, EXT_MESH_FEATURES: () => name, EXT_STRUCTURAL_METADATA: () => name2, GLBLoader: () => GLBLoader, GLBWriter: () => GLBWriter, GLTFLoader: () => GLTFLoader, GLTFScenegraph: () => GLTFScenegraph, GLTFWriter: () => GLTFWriter, _getMemoryUsageGLTF: () => getMemoryUsageGLTF, createExtMeshFeatures: () => createExtMeshFeatures, createExtStructuralMetadata: () => createExtStructuralMetadata, postProcessGLTF: () => postProcessGLTF }); module.exports = __toCommonJS(dist_exports); // dist/lib/extensions/EXT_mesh_features.js var EXT_mesh_features_exports = {}; __export(EXT_mesh_features_exports, { createExtMeshFeatures: () => createExtMeshFeatures, decode: () => decode, encode: () => encode, name: () => name }); // dist/lib/api/gltf-scenegraph.js var import_images = require("@loaders.gl/images"); var import_loader_utils = require("@loaders.gl/loader-utils"); // dist/lib/utils/assert.js function assert(condition, message) { if (!condition) { throw new Error(message || "assert failed: gltf"); } } // dist/lib/gltf-utils/gltf-constants.js var COMPONENTS = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 }; var BYTES = { 5120: 1, // BYTE 5121: 1, // UNSIGNED_BYTE 5122: 2, // SHORT 5123: 2, // UNSIGNED_SHORT 5125: 4, // UNSIGNED_INT 5126: 4 // FLOAT }; // dist/lib/gltf-utils/gltf-utils.js var MIPMAP_FACTOR = 1.33; var TYPES = ["SCALAR", "VEC2", "VEC3", "VEC4"]; var ARRAY_CONSTRUCTOR_TO_WEBGL_CONSTANT = [ [Int8Array, 5120], [Uint8Array, 5121], [Int16Array, 5122], [Uint16Array, 5123], [Uint32Array, 5125], [Float32Array, 5126], [Float64Array, 5130] ]; var ARRAY_TO_COMPONENT_TYPE = new Map(ARRAY_CONSTRUCTOR_TO_WEBGL_CONSTANT); var ATTRIBUTE_TYPE_TO_COMPONENTS = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 }; var ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE = { 5120: 1, 5121: 1, 5122: 2, 5123: 2, 5125: 4, 5126: 4 }; var ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = { 5120: Int8Array, 5121: Uint8Array, 5122: Int16Array, 5123: Uint16Array, 5125: Uint32Array, 5126: Float32Array }; function getAccessorTypeFromSize(size) { const type = TYPES[size - 1]; return type || TYPES[0]; } function getComponentTypeFromArray(typedArray) { const componentType = ARRAY_TO_COMPONENT_TYPE.get(typedArray.constructor); if (!componentType) { throw new Error("Illegal typed array"); } return componentType; } function getAccessorArrayTypeAndLength(accessor, bufferView) { const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[accessor.componentType]; const components = ATTRIBUTE_TYPE_TO_COMPONENTS[accessor.type]; const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[accessor.componentType]; const length = accessor.count * components; const byteLength = accessor.count * components * bytesPerComponent; assert(byteLength >= 0 && byteLength <= bufferView.byteLength); const componentByteSize = BYTES[accessor.componentType]; const numberOfComponentsInElement = COMPONENTS[accessor.type]; return { ArrayType, length, byteLength, componentByteSize, numberOfComponentsInElement }; } function getMemoryUsageGLTF(gltf) { let { images, bufferViews } = gltf; images = images || []; bufferViews = bufferViews || []; const imageBufferViews = images.map((i) => i.bufferView); bufferViews = bufferViews.filter((view) => !imageBufferViews.includes(view)); const bufferMemory = bufferViews.reduce((acc, view) => acc + view.byteLength, 0); const pixelCount = images.reduce((acc, image) => { const { width, height } = image.image; return acc + width * height; }, 0); return bufferMemory + Math.ceil(4 * pixelCount * MIPMAP_FACTOR); } // dist/lib/gltf-utils/get-typed-array.js function getTypedArrayForBufferView(json, buffers, bufferViewIndex) { const bufferView = json.bufferViews[bufferViewIndex]; assert(bufferView); const bufferIndex = bufferView.buffer; const binChunk = buffers[bufferIndex]; assert(binChunk); const byteOffset = (bufferView.byteOffset || 0) + binChunk.byteOffset; return new Uint8Array(binChunk.arrayBuffer, byteOffset, bufferView.byteLength); } function getTypedArrayForAccessor(json, buffers, accessor) { var _a, _b; const gltfAccessor = typeof accessor === "number" ? (_a = json.accessors) == null ? void 0 : _a[accessor] : accessor; if (!gltfAccessor) { throw new Error(`No gltf accessor ${JSON.stringify(accessor)}`); } const bufferView = (_b = json.bufferViews) == null ? void 0 : _b[gltfAccessor.bufferView || 0]; if (!bufferView) { throw new Error(`No gltf buffer view for accessor ${bufferView}`); } const { arrayBuffer, byteOffset: bufferByteOffset } = buffers[bufferView.buffer]; const byteOffset = (bufferByteOffset || 0) + (gltfAccessor.byteOffset || 0) + (bufferView.byteOffset || 0); const { ArrayType, length, componentByteSize, numberOfComponentsInElement } = getAccessorArrayTypeAndLength(gltfAccessor, bufferView); const elementByteSize = componentByteSize * numberOfComponentsInElement; const elementAddressScale = bufferView.byteStride || elementByteSize; if (typeof bufferView.byteStride === "undefined" || bufferView.byteStride === elementByteSize) { const result2 = new ArrayType(arrayBuffer, byteOffset, length); return result2; } const result = new ArrayType(length); for (let i = 0; i < gltfAccessor.count; i++) { const values = new ArrayType(arrayBuffer, byteOffset + i * elementAddressScale, numberOfComponentsInElement); result.set(values, i * numberOfComponentsInElement); } return result; } // dist/lib/api/gltf-scenegraph.js function makeDefaultGLTFJson() { return { asset: { version: "2.0", generator: "loaders.gl" }, buffers: [], extensions: {}, extensionsRequired: [], extensionsUsed: [] }; } var GLTFScenegraph = class { // internal gltf; sourceBuffers; byteLength; // TODO - why is this not GLTFWithBuffers - what happens to images? constructor(gltf) { this.gltf = { json: (gltf == null ? void 0 : gltf.json) || makeDefaultGLTFJson(), buffers: (gltf == null ? void 0 : gltf.buffers) || [], images: (gltf == null ? void 0 : gltf.images) || [] }; this.sourceBuffers = []; this.byteLength = 0; if (this.gltf.buffers && this.gltf.buffers[0]) { this.byteLength = this.gltf.buffers[0].byteLength; this.sourceBuffers = [this.gltf.buffers[0]]; } } // Accessors get json() { return this.gltf.json; } getApplicationData(key) { const data = this.json[key]; return data; } getExtraData(key) { const extras = this.json.extras || {}; return extras[key]; } hasExtension(extensionName) { const isUsedExtension = this.getUsedExtensions().find((name12) => name12 === extensionName); const isRequiredExtension = this.getRequiredExtensions().find((name12) => name12 === extensionName); return typeof isUsedExtension === "string" || typeof isRequiredExtension === "string"; } getExtension(extensionName) { const isExtension = this.getUsedExtensions().find((name12) => name12 === extensionName); const extensions = this.json.extensions || {}; return isExtension ? extensions[extensionName] : null; } getRequiredExtension(extensionName) { const isRequired = this.getRequiredExtensions().find((name12) => name12 === extensionName); return isRequired ? this.getExtension(extensionName) : null; } getRequiredExtensions() { return this.json.extensionsRequired || []; } getUsedExtensions() { return this.json.extensionsUsed || []; } getRemovedExtensions() { return this.json.extensionsRemoved || []; } getObjectExtension(object, extensionName) { const extensions = object.extensions || {}; return extensions[extensionName]; } getScene(index) { return this.getObject("scenes", index); } getNode(index) { return this.getObject("nodes", index); } getSkin(index) { return this.getObject("skins", index); } getMesh(index) { return this.getObject("meshes", index); } getMaterial(index) { return this.getObject("materials", index); } getAccessor(index) { return this.getObject("accessors", index); } // getCamera(index: number): object | null { // return null; // TODO: fix thi: object as null; // } getTexture(index) { return this.getObject("textures", index); } getSampler(index) { return this.getObject("samplers", index); } getImage(index) { return this.getObject("images", index); } getBufferView(index) { return this.getObject("bufferViews", index); } getBuffer(index) { return this.getObject("buffers", index); } getObject(array, index) { if (typeof index === "object") { return index; } const object = this.json[array] && this.json[array][index]; if (!object) { throw new Error(`glTF file error: Could not find ${array}[${index}]`); } return object; } /** * Accepts buffer view index or buffer view object * @returns a `Uint8Array` */ getTypedArrayForBufferView(bufferView) { bufferView = this.getBufferView(bufferView); const bufferIndex = bufferView.buffer; const binChunk = this.gltf.buffers[bufferIndex]; assert(binChunk); const byteOffset = (bufferView.byteOffset || 0) + binChunk.byteOffset; return new Uint8Array(binChunk.arrayBuffer, byteOffset, bufferView.byteLength); } /** Accepts accessor index or accessor object * @returns a typed array with type that matches the types */ getTypedArrayForAccessor(accessor) { const gltfAccessor = this.getAccessor(accessor); return getTypedArrayForAccessor(this.gltf.json, this.gltf.buffers, gltfAccessor); } /** accepts accessor index or accessor object * returns a `Uint8Array` */ getTypedArrayForImageData(image) { image = this.getAccessor(image); const bufferView = this.getBufferView(image.bufferView); const buffer = this.getBuffer(bufferView.buffer); const arrayBuffer = buffer.data; const byteOffset = bufferView.byteOffset || 0; return new Uint8Array(arrayBuffer, byteOffset, bufferView.byteLength); } // MODIFERS /** * Add an extra application-defined key to the top-level data structure */ addApplicationData(key, data) { this.json[key] = data; return this; } /** * `extras` - Standard GLTF field for storing application specific data */ addExtraData(key, data) { this.json.extras = this.json.extras || {}; this.json.extras[key] = data; return this; } addObjectExtension(object, extensionName, data) { object.extensions = object.extensions || {}; object.extensions[extensionName] = data; this.registerUsedExtension(extensionName); return this; } setObjectExtension(object, extensionName, data) { const extensions = object.extensions || {}; extensions[extensionName] = data; } removeObjectExtension(object, extensionName) { const extensions = (object == null ? void 0 : object.extensions) || {}; if (extensions[extensionName]) { this.json.extensionsRemoved = this.json.extensionsRemoved || []; const extensionsRemoved = this.json.extensionsRemoved; if (!extensionsRemoved.includes(extensionName)) { extensionsRemoved.push(extensionName); } } delete extensions[extensionName]; } /** * Add to standard GLTF top level extension object, mark as used */ addExtension(extensionName, extensionData = {}) { assert(extensionData); this.json.extensions = this.json.extensions || {}; this.json.extensions[extensionName] = extensionData; this.registerUsedExtension(extensionName); return extensionData; } /** * Standard GLTF top level extension object, mark as used and required */ addRequiredExtension(extensionName, extensionData = {}) { assert(extensionData); this.addExtension(extensionName, extensionData); this.registerRequiredExtension(extensionName); return extensionData; } /** * Add extensionName to list of used extensions */ registerUsedExtension(extensionName) { this.json.extensionsUsed = this.json.extensionsUsed || []; if (!this.json.extensionsUsed.find((ext) => ext === extensionName)) { this.json.extensionsUsed.push(extensionName); } } /** * Add extensionName to list of required extensions */ registerRequiredExtension(extensionName) { this.registerUsedExtension(extensionName); this.json.extensionsRequired = this.json.extensionsRequired || []; if (!this.json.extensionsRequired.find((ext) => ext === extensionName)) { this.json.extensionsRequired.push(extensionName); } } /** * Removes an extension from the top-level list */ removeExtension(extensionName) { var _a; if ((_a = this.json.extensions) == null ? void 0 : _a[extensionName]) { this.json.extensionsRemoved = this.json.extensionsRemoved || []; const extensionsRemoved = this.json.extensionsRemoved; if (!extensionsRemoved.includes(extensionName)) { extensionsRemoved.push(extensionName); } } if (this.json.extensions) { delete this.json.extensions[extensionName]; } if (this.json.extensionsRequired) { this._removeStringFromArray(this.json.extensionsRequired, extensionName); } if (this.json.extensionsUsed) { this._removeStringFromArray(this.json.extensionsUsed, extensionName); } } /** * Set default scene which is to be displayed at load time */ setDefaultScene(sceneIndex) { this.json.scene = sceneIndex; } /** * @todo: add more properties for scene initialization: * name`, `extensions`, `extras` * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-scene */ addScene(scene) { const { nodeIndices } = scene; this.json.scenes = this.json.scenes || []; this.json.scenes.push({ nodes: nodeIndices }); return this.json.scenes.length - 1; } /** * @todo: add more properties for node initialization: * `name`, `extensions`, `extras`, `camera`, `children`, `skin`, `rotation`, `scale`, `translation`, `weights` * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#node */ addNode(node) { const { meshIndex, matrix } = node; this.json.nodes = this.json.nodes || []; const nodeData = { mesh: meshIndex }; if (matrix) { nodeData.matrix = matrix; } this.json.nodes.push(nodeData); return this.json.nodes.length - 1; } /** Adds a mesh to the json part */ addMesh(mesh) { const { attributes, indices, material, mode = 4 } = mesh; const accessors = this._addAttributes(attributes); const glTFMesh = { primitives: [ { attributes: accessors, mode } ] }; if (indices) { const indicesAccessor = this._addIndices(indices); glTFMesh.primitives[0].indices = indicesAccessor; } if (Number.isFinite(material)) { glTFMesh.primitives[0].material = material; } this.json.meshes = this.json.meshes || []; this.json.meshes.push(glTFMesh); return this.json.meshes.length - 1; } addPointCloud(attributes) { const accessorIndices = this._addAttributes(attributes); const glTFMesh = { primitives: [ { attributes: accessorIndices, mode: 0 // GL.POINTS } ] }; this.json.meshes = this.json.meshes || []; this.json.meshes.push(glTFMesh); return this.json.meshes.length - 1; } /** * Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference * Buffer will be copied into BIN chunk during "pack" * Currently encodes as glTF image * @param imageData * @param mimeType */ addImage(imageData, mimeTypeOpt) { const metadata = (0, import_images.getBinaryImageMetadata)(imageData); const mimeType = mimeTypeOpt || (metadata == null ? void 0 : metadata.mimeType); const bufferViewIndex = this.addBufferView(imageData); const glTFImage = { bufferView: bufferViewIndex, mimeType }; this.json.images = this.json.images || []; this.json.images.push(glTFImage); return this.json.images.length - 1; } /** * Add one untyped source buffer, create a matching glTF `bufferView`, and return its index * @param buffer */ addBufferView(buffer, bufferIndex = 0, byteOffset = this.byteLength) { const byteLength = buffer.byteLength; assert(Number.isFinite(byteLength)); this.sourceBuffers = this.sourceBuffers || []; this.sourceBuffers.push(buffer); const glTFBufferView = { buffer: bufferIndex, // Write offset from the start of the binary body byteOffset, byteLength }; this.byteLength += (0, import_loader_utils.padToNBytes)(byteLength, 4); this.json.bufferViews = this.json.bufferViews || []; this.json.bufferViews.push(glTFBufferView); return this.json.bufferViews.length - 1; } /** * Adds an accessor to a bufferView * @param bufferViewIndex * @param accessor */ addAccessor(bufferViewIndex, accessor) { const glTFAccessor = { bufferView: bufferViewIndex, // @ts-ignore type: getAccessorTypeFromSize(accessor.size), // @ts-ignore componentType: accessor.componentType, // @ts-ignore count: accessor.count, // @ts-ignore max: accessor.max, // @ts-ignore min: accessor.min }; this.json.accessors = this.json.accessors || []; this.json.accessors.push(glTFAccessor); return this.json.accessors.length - 1; } /** * Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference * Buffer will be copied into BIN chunk during "pack" * Currently encodes buffers as glTF accessors, but this could be optimized * @param sourceBuffer * @param accessor */ addBinaryBuffer(sourceBuffer, accessor = { size: 3 }) { const bufferViewIndex = this.addBufferView(sourceBuffer); let minMax = { min: accessor.min, max: accessor.max }; if (!minMax.min || !minMax.max) { minMax = this._getAccessorMinMax(sourceBuffer, accessor.size); } const accessorDefaults = { // @ts-ignore size: accessor.size, componentType: getComponentTypeFromArray(sourceBuffer), // @ts-ignore count: Math.round(sourceBuffer.length / accessor.size), min: minMax.min, max: minMax.max }; return this.addAccessor(bufferViewIndex, Object.assign(accessorDefaults, accessor)); } /** * Adds a texture to the json part * @todo: add more properties for texture initialization * `sampler`, `name`, `extensions`, `extras` * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#texture */ addTexture(texture) { const { imageIndex } = texture; const glTFTexture = { source: imageIndex }; this.json.textures = this.json.textures || []; this.json.textures.push(glTFTexture); return this.json.textures.length - 1; } /** Adds a material to the json part */ addMaterial(pbrMaterialInfo) { this.json.materials = this.json.materials || []; this.json.materials.push(pbrMaterialInfo); return this.json.materials.length - 1; } /** Pack the binary chunk */ createBinaryChunk() { var _a, _b; const totalByteLength = this.byteLength; const arrayBuffer = new ArrayBuffer(totalByteLength); const targetArray = new Uint8Array(arrayBuffer); let dstByteOffset = 0; for (const sourceBuffer of this.sourceBuffers || []) { dstByteOffset = (0, import_loader_utils.copyToArray)(sourceBuffer, targetArray, dstByteOffset); } if ((_b = (_a = this.json) == null ? void 0 : _a.buffers) == null ? void 0 : _b[0]) { this.json.buffers[0].byteLength = totalByteLength; } else { this.json.buffers = [{ byteLength: totalByteLength }]; } this.gltf.binary = arrayBuffer; this.sourceBuffers = [arrayBuffer]; this.gltf.buffers = [{ arrayBuffer, byteOffset: 0, byteLength: arrayBuffer.byteLength }]; } // PRIVATE _removeStringFromArray(array, string) { let found = true; while (found) { const index = array.indexOf(string); if (index > -1) { array.splice(index, 1); } else { found = false; } } } /** * Add attributes to buffers and create `attributes` object which is part of `mesh` */ _addAttributes(attributes = {}) { const result = {}; for (const attributeKey in attributes) { const attributeData = attributes[attributeKey]; const attrName = this._getGltfAttributeName(attributeKey); const accessor = this.addBinaryBuffer(attributeData.value, attributeData); result[attrName] = accessor; } return result; } /** * Add indices to buffers */ _addIndices(indices) { return this.addBinaryBuffer(indices, { size: 1 }); } /** * Deduce gltf specific attribue name from input attribute name */ _getGltfAttributeName(attributeName) { switch (attributeName.toLowerCase()) { case "position": case "positions": case "vertices": return "POSITION"; case "normal": case "normals": return "NORMAL"; case "color": case "colors": return "COLOR_0"; case "texcoord": case "texcoords": return "TEXCOORD_0"; default: return attributeName; } } /** * Calculate `min` and `max` arrays of accessor according to spec: * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-accessor */ _getAccessorMinMax(buffer, size) { const result = { min: null, max: null }; if (buffer.length < size) { return result; } result.min = []; result.max = []; const initValues = buffer.subarray(0, size); for (const value of initValues) { result.min.push(value); result.max.push(value); } for (let index = size; index < buffer.length; index += size) { for (let componentIndex = 0; componentIndex < size; componentIndex++) { result.min[0 + componentIndex] = Math.min( // @ts-ignore result.min[0 + componentIndex], buffer[index + componentIndex] ); result.max[0 + componentIndex] = Math.max( // @ts-ignore result.max[0 + componentIndex], buffer[index + componentIndex] ); } } return result; } }; // dist/lib/extensions/utils/3d-tiles-utils.js var import_images2 = require("@loaders.gl/images"); var import_loader_utils2 = require("@loaders.gl/loader-utils"); function emod(n) { return (n % 1 + 1) % 1; } var ATTRIBUTE_TYPE_TO_COMPONENTS2 = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16, BOOLEAN: 1, STRING: 1, ENUM: 1 }; var ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY2 = { INT8: Int8Array, UINT8: Uint8Array, INT16: Int16Array, UINT16: Uint16Array, INT32: Int32Array, UINT32: Uint32Array, INT64: BigInt64Array, UINT64: BigUint64Array, FLOAT32: Float32Array, FLOAT64: Float64Array }; var ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE2 = { INT8: 1, UINT8: 1, INT16: 2, UINT16: 2, INT32: 4, UINT32: 4, INT64: 8, UINT64: 8, FLOAT32: 4, FLOAT64: 8 }; function getArrayElementByteSize(attributeType, componentType) { return ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE2[componentType] * ATTRIBUTE_TYPE_TO_COMPONENTS2[attributeType]; } function getOffsetsForProperty(scenegraph, bufferViewIndex, offsetType, numberOfElements) { if (offsetType !== "UINT8" && offsetType !== "UINT16" && offsetType !== "UINT32" && offsetType !== "UINT64") { return null; } const arrayOffsetsBytes = scenegraph.getTypedArrayForBufferView(bufferViewIndex); const arrayOffsets = convertRawBufferToMetadataArray( arrayOffsetsBytes, "SCALAR", // offsets consist of ONE component offsetType, numberOfElements + 1 // The number of offsets is equal to the property table `count` plus one. ); if (arrayOffsets instanceof BigInt64Array || arrayOffsets instanceof BigUint64Array) { return null; } return arrayOffsets; } function convertRawBufferToMetadataArray(data, attributeType, componentType, elementCount = 1) { const numberOfComponents = ATTRIBUTE_TYPE_TO_COMPONENTS2[attributeType]; const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY2[componentType]; const size = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE2[componentType]; const length = elementCount * numberOfComponents; const byteLength = length * size; let buffer = data.buffer; let offset = data.byteOffset; if (offset % size !== 0) { const bufferArray = new Uint8Array(buffer); buffer = bufferArray.slice(offset, offset + byteLength).buffer; offset = 0; } return new ArrayType((0, import_loader_utils2.ensureArrayBuffer)(buffer), offset, length); } function getPrimitiveTextureData(scenegraph, textureInfo, primitive) { var _a, _b, _c, _d, _e; const texCoordAccessorKey = `TEXCOORD_${textureInfo.texCoord || 0}`; const texCoordAccessorIndex = primitive.attributes[texCoordAccessorKey]; const textureCoordinates = scenegraph.getTypedArrayForAccessor(texCoordAccessorIndex); const json = scenegraph.gltf.json; const textureIndex = textureInfo.index; const imageIndex = (_b = (_a = json.textures) == null ? void 0 : _a[textureIndex]) == null ? void 0 : _b.source; if (typeof imageIndex !== "undefined") { const mimeType = (_d = (_c = json.images) == null ? void 0 : _c[imageIndex]) == null ? void 0 : _d.mimeType; const parsedImage = (_e = scenegraph.gltf.images) == null ? void 0 : _e[imageIndex]; if (parsedImage && typeof parsedImage.width !== "undefined") { const textureData = []; for (let index = 0; index < textureCoordinates.length; index += 2) { const value = getImageValueByCoordinates(parsedImage, mimeType, textureCoordinates, index, textureInfo.channels); textureData.push(value); } return textureData; } } return []; } function primitivePropertyDataToAttributes(scenegraph, attributeName, propertyData, featureTable, primitive) { if (!(propertyData == null ? void 0 : propertyData.length)) { return; } const featureIndices = []; for (const texelData of propertyData) { let index = featureTable.findIndex((item) => item === texelData); if (index === -1) { index = featureTable.push(texelData) - 1; } featureIndices.push(index); } const typedArray = new Uint32Array(featureIndices); const bufferIndex = scenegraph.gltf.buffers.push({ arrayBuffer: typedArray.buffer, byteOffset: typedArray.byteOffset, byteLength: typedArray.byteLength }) - 1; const bufferViewIndex = scenegraph.addBufferView(typedArray, bufferIndex, 0); const accessorIndex = scenegraph.addAccessor(bufferViewIndex, { size: 1, componentType: getComponentTypeFromArray(typedArray), count: typedArray.length }); primitive.attributes[attributeName] = accessorIndex; } function getImageValueByCoordinates(parsedImage, mimeType, textureCoordinates, index, channels = [0]) { const CHANNELS_MAP = { r: { offset: 0, shift: 0 }, g: { offset: 1, shift: 8 }, b: { offset: 2, shift: 16 }, a: { offset: 3, shift: 24 } }; const u = textureCoordinates[index]; const v = textureCoordinates[index + 1]; let components = 1; if (mimeType && (mimeType.indexOf("image/jpeg") !== -1 || mimeType.indexOf("image/png") !== -1)) components = 4; const offset = coordinatesToOffset(u, v, parsedImage, components); let value = 0; for (const c of channels) { const map = typeof c === "number" ? Object.values(CHANNELS_MAP)[c] : CHANNELS_MAP[c]; const imageOffset = offset + map.offset; const imageData = (0, import_images2.getImageData)(parsedImage); if (imageData.data.length <= imageOffset) { throw new Error(`${imageData.data.length} <= ${imageOffset}`); } const imageValue = imageData.data[imageOffset]; value |= imageValue << map.shift; } return value; } function coordinatesToOffset(u, v, parsedImage, componentsCount = 1) { const w = parsedImage.width; const iX = emod(u) * (w - 1); const indX = Math.round(iX); const h = parsedImage.height; const iY = emod(v) * (h - 1); const indY = Math.round(iY); const components = parsedImage.components ? parsedImage.components : componentsCount; const offset = (indY * w + indX) * components; return offset; } function parseVariableLengthArrayNumeric(valuesData, numberOfElements, arrayOffsets, valuesDataBytesLength, valueSize) { const attributeValueArray = []; for (let index = 0; index < numberOfElements; index++) { const arrayOffset = arrayOffsets[index]; const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index]; if (arrayByteSize + arrayOffset > valuesDataBytesLength) { break; } const typedArrayOffset = arrayOffset / valueSize; const elementCount = arrayByteSize / valueSize; attributeValueArray.push(valuesData.slice(typedArrayOffset, typedArrayOffset + elementCount)); } return attributeValueArray; } function parseFixedLengthArrayNumeric(valuesData, numberOfElements, arrayCount) { const attributeValueArray = []; for (let index = 0; index < numberOfElements; index++) { const elementOffset = index * arrayCount; attributeValueArray.push(valuesData.slice(elementOffset, elementOffset + arrayCount)); } return attributeValueArray; } function getPropertyDataString(numberOfElements, valuesDataBytes, arrayOffsets, stringOffsets) { if (arrayOffsets) { throw new Error("Not implemented - arrayOffsets for strings is specified"); } if (stringOffsets) { const stringsArray = []; const textDecoder = new TextDecoder("utf8"); let stringOffset = 0; for (let index = 0; index < numberOfElements; index++) { const stringByteSize = stringOffsets[index + 1] - stringOffsets[index]; if (stringByteSize + stringOffset <= valuesDataBytes.length) { const stringData = valuesDataBytes.subarray(stringOffset, stringByteSize + stringOffset); const stringAttribute = textDecoder.decode(stringData); stringsArray.push(stringAttribute); stringOffset += stringByteSize; } } return stringsArray; } return []; } // dist/lib/extensions/EXT_mesh_features.js var EXT_MESH_FEATURES_NAME = "EXT_mesh_features"; var name = EXT_MESH_FEATURES_NAME; async function decode(gltfData, options) { const scenegraph = new GLTFScenegraph(gltfData); decodeExtMeshFeatures(scenegraph, options); } function encode(gltfData, options) { const scenegraph = new GLTFScenegraph(gltfData); encodeExtMeshFeatures(scenegraph, options); scenegraph.createBinaryChunk(); return scenegraph.gltf; } function decodeExtMeshFeatures(scenegraph, options) { const json = scenegraph.gltf.json; if (!json.meshes) { return; } for (const mesh of json.meshes) { for (const primitive of mesh.primitives) { processMeshPrimitiveFeatures(scenegraph, primitive, options); } } } function processMeshPrimitiveFeatures(scenegraph, primitive, options) { var _a, _b, _c; if (!((_a = options == null ? void 0 : options.gltf) == null ? void 0 : _a.loadBuffers)) { return; } const extension = (_b = primitive.extensions) == null ? void 0 : _b[EXT_MESH_FEATURES_NAME]; const featureIds = extension == null ? void 0 : extension.featureIds; if (!featureIds) { return; } for (const featureId of featureIds) { let featureIdData; if (typeof featureId.attribute !== "undefined") { const accessorKey = `_FEATURE_ID_${featureId.attribute}`; const accessorIndex = primitive.attributes[accessorKey]; featureIdData = scenegraph.getTypedArrayForAccessor(accessorIndex); } else if (typeof featureId.texture !== "undefined" && ((_c = options == null ? void 0 : options.gltf) == null ? void 0 : _c.loadImages)) { featureIdData = getPrimitiveTextureData(scenegraph, featureId.texture, primitive); } else { featureIdData = []; } featureId.data = featureIdData; } } function encodeExtMeshFeatures(scenegraph, options) { const meshes = scenegraph.gltf.json.meshes; if (!meshes) { return; } for (const mesh of meshes) { for (const primitive of mesh.primitives) { encodeExtMeshFeaturesForPrimitive(scenegraph, primitive); } } } function createExtMeshFeatures(scenegraph, primitive, featureIdArray, propertyTableIndex) { if (!primitive.extensions) { primitive.extensions = {}; } let extension = primitive.extensions[EXT_MESH_FEATURES_NAME]; if (!extension) { extension = { featureIds: [] }; primitive.extensions[EXT_MESH_FEATURES_NAME] = extension; } const { featureIds } = extension; const featureId = { featureCount: featureIdArray.length, propertyTable: propertyTableIndex, data: featureIdArray }; featureIds.push(featureId); scenegraph.addObjectExtension(primitive, EXT_MESH_FEATURES_NAME, extension); } function encodeExtMeshFeaturesForPrimitive(scenegraph, primitive) { var _a; const extension = (_a = primitive.extensions) == null ? void 0 : _a[EXT_MESH_FEATURES_NAME]; if (!extension) { return; } const featureIds = extension.featureIds; featureIds.forEach((featureId, elementIndex) => { if (featureId.data) { const { accessorKey, index } = createAccessorKey(primitive.attributes); const typedArray = new Uint32Array(featureId.data); featureIds[elementIndex] = { featureCount: typedArray.length, propertyTable: featureId.propertyTable, attribute: index }; scenegraph.gltf.buffers.push({ arrayBuffer: typedArray.buffer, byteOffset: typedArray.byteOffset, byteLength: typedArray.byteLength }); const bufferViewIndex = scenegraph.addBufferView(typedArray); const accessorIndex = scenegraph.addAccessor(bufferViewIndex, { size: 1, componentType: getComponentTypeFromArray(typedArray), count: typedArray.length }); primitive.attributes[accessorKey] = accessorIndex; } }); } function createAccessorKey(attributes) { const prefix = "_FEATURE_ID_"; const attrs = Object.keys(attributes).filter((item) => item.indexOf(prefix) === 0); let max = -1; for (const a of attrs) { const n = Number(a.substring(prefix.length)); if (n > max) { max = n; } } max++; const accessorKey = `${prefix}${max}`; return { accessorKey, index: max }; } // dist/lib/extensions/EXT_structural_metadata.js var EXT_structural_metadata_exports = {}; __export(EXT_structural_metadata_exports, { createExtStructuralMetadata: () => createExtStructuralMetadata, decode: () => decode2, encode: () => encode2, name: () => name2 }); var import_loader_utils3 = require("@loaders.gl/loader-utils"); var EXT_STRUCTURAL_METADATA_NAME = "EXT_structural_metadata"; var name2 = EXT_STRUCTURAL_METADATA_NAME; async function decode2(gltfData, options) { const scenegraph = new GLTFScenegraph(gltfData); decodeExtStructuralMetadata(scenegraph, options); } function encode2(gltfData, options) { const scenegraph = new GLTFScenegraph(gltfData); encodeExtStructuralMetadata(scenegraph, options); scenegraph.createBinaryChunk(); return scenegraph.gltf; } function decodeExtStructuralMetadata(scenegraph, options) { var _a, _b; if (!((_a = options.gltf) == null ? void 0 : _a.loadBuffers)) { return; } const extension = scenegraph.getExtension(EXT_STRUCTURAL_METADATA_NAME); if (!extension) { return; } if ((_b = options.gltf) == null ? void 0 : _b.loadImages) { decodePropertyTextures(scenegraph, extension); } decodePropertyTables(scenegraph, extension); } function decodePropertyTextures(scenegraph, extension) { const propertyTextures = extension.propertyTextures; const json = scenegraph.gltf.json; if (propertyTextures && json.meshes) { for (const mesh of json.meshes) { for (const primitive of mesh.primitives) { processPrimitivePropertyTextures(scenegraph, propertyTextures, primitive, extension); } } } } function decodePropertyTables(scenegraph, extension) { const schema = extension.schema; if (!schema) { return; } const schemaClasses = schema.classes; const propertyTables = extension.propertyTables; if (schemaClasses && propertyTables) { for (const schemaName in schemaClasses) { const propertyTable = findPropertyTableByClass(propertyTables, schemaName); if (propertyTable) { processPropertyTable(scenegraph, schema, propertyTable); } } } } function findPropertyTableByClass(propertyTables, schemaClassName) { for (const propertyTable of propertyTables) { if (propertyTable.class === schemaClassName) { return propertyTable; } } return null; } function processPrimitivePropertyTextures(scenegraph, propertyTextures, primitive, extension) { var _a; if (!propertyTextures) { return; } const primitiveExtension = (_a = primitive.extensions) == null ? void 0 : _a[EXT_STRUCTURAL_METADATA_NAME]; const primitivePropertyTextureIndices = primitiveExtension == null ? void 0 : primitiveExtension.propertyTextures; if (!primitivePropertyTextureIndices) { return; } for (const primitivePropertyTextureIndex of primitivePropertyTextureIndices) { const propertyTexture = propertyTextures[primitivePropertyTextureIndex]; processPrimitivePropertyTexture(scenegraph, propertyTexture, primitive, extension); } } function processPrimitivePropertyTexture(scenegraph, propertyTexture, primitive, extension) { var _a; if (!propertyTexture.properties) { return; } if (!extension.dataAttributeNames) { extension.dataAttributeNames = []; } const className = propertyTexture.class; for (const propertyName in propertyTexture.properties) { const attributeName = `${className}_${propertyName}`; const textureInfoTopLevel = (_a = propertyTexture.properties) == null ? void 0 : _a[propertyName]; if (!textureInfoTopLevel) { continue; } if (!textureInfoTopLevel.data) { textureInfoTopLevel.data = []; } const featureTextureTable = textureInfoTopLevel.data; const propertyData = getPrimitiveTextureData(scenegraph, textureInfoTopLevel, primitive); if (propertyData === null) { continue; } primitivePropertyDataToAttributes(scenegraph, attributeName, propertyData, featureTextureTable, primitive); textureInfoTopLevel.data = featureTextureTable; extension.dataAttributeNames.push(attributeName); } } function processPropertyTable(scenegraph, schema, propertyTable) { var _a, _b; const schemaClass = (_a = schema.classes) == null ? void 0 : _a[propertyTable.class]; if (!schemaClass) { throw new Error(`Incorrect data in the EXT_structural_metadata extension: no schema class with name ${propertyTable.class}`); } const numberOfElements = propertyTable.count; for (const propertyName in schemaClass.properties) { const classProperty = schemaClass.properties[propertyName]; const propertyTableProperty = (_b = propertyTable.properties) == null ? void 0 : _b[propertyName]; if (propertyTableProperty) { const data = getPropertyDataFromBinarySource(scenegraph, schema, classProperty, numberOfElements, propertyTableProperty); propertyTableProperty.data = data; } } } function getPropertyDataFromBinarySource(scenegraph, schema, classProperty, numberOfElements, propertyTableProperty) { let data = []; const valuesBufferView = propertyTableProperty.values; const valuesDataBytes = scenegraph.getTypedArrayForBufferView(valuesBufferView); const arrayOffsets = getArrayOffsetsForProperty(scenegraph, classProperty, propertyTableProperty, numberOfElements); const stringOffsets = getStringOffsetsForProperty(scenegraph, propertyTableProperty, numberOfElements); switch (classProperty.type) { case "SCALAR": case "VEC2": case "VEC3": case "VEC4": case "MAT2": case "MAT3": case "MAT4": { data = getPropertyDataNumeric(classProperty, numberOfElements, valuesDataBytes, arrayOffsets); break; } case "BOOLEAN": { throw new Error(`Not implemented - classProperty.type=${classProperty.type}`); } case "STRING": { data = getPropertyDataString(numberOfElements, valuesDataBytes, arrayOffsets, stringOffsets); break; } case "ENUM": { data = getPropertyDataENUM(schema, classProperty, numberOfElements, valuesDataBytes, arrayOffsets); break; } default: throw new Error(`Unknown classProperty type ${classProperty.type}`); } return data; } function getArrayOffsetsForProperty(scenegraph, classProperty, propertyTableProperty, numberOfElements) { if (classProperty.array && // `count` is a number of array elements. May only be defined when `array` is true. // If `count` is NOT defined, it's a VARIABLE-length array typeof classProperty.count === "undefined" && // `arrayOffsets` is an index of the buffer view containing offsets for variable-length arrays. typeof propertyTableProperty.arrayOffsets !== "undefined") { return getOffsetsForProperty(scenegraph, propertyTableProperty.arrayOffsets, propertyTableProperty.arrayOffsetType || "UINT32", numberOfElements); } return null; } function getStringOffsetsForProperty(scenegraph, propertyTableProperty, numberOfElements) { if (typeof propertyTableProperty.stringOffsets !== "undefined") { return getOffsetsForProperty(scenegraph, propertyTableProperty.stringOffsets, propertyTableProperty.stringOffsetType || "UINT32", numberOfElements); } return null; } function getPropertyDataNumeric(classProperty, numberOfElements, valuesDataBytes, arrayOffsets) { const isArray = classProperty.array; const arrayCount = classProperty.count; const elementSize = getArrayElementByteSize(classProperty.type, classProperty.componentType); const elementCount = valuesDataBytes.byteLength / elementSize; let valuesData; if (classProperty.componentType) { valuesData = convertRawBufferToMetadataArray( valuesDataBytes, classProperty.type, // The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types. classProperty.componentType, elementCount ); } else { valuesData = valuesDataBytes; } if (isArray) { if (arrayOffsets) { return parseVariableLengthArrayNumeric(valuesData, numberOfElements, arrayOffsets, valuesDataBytes.length, elementSize); } if (arrayCount) { return parseFixedLengthArrayNumeric(valuesData, numberOfElements, arrayCount); } return []; } return valuesData; } function getPropertyDataENUM(schema, classProperty, numberOfElements, valuesDataBytes, arrayOffsets) { var _a; const enumType = classProperty.enumType; if (!enumType) { throw new Error("Incorrect data in the EXT_structural_metadata extension: classProperty.enumType is not set for type ENUM"); } const enumEntry = (_a = schema.enums) == null ? void 0 : _a[enumType]; if (!enumEntry) { throw new Error(`Incorrect data in the EXT_structural_metadata extension: schema.enums does't contain ${enumType}`); } const enumValueType = enumEntry.valueType || "UINT16"; const elementSize = getArrayElementByteSize(classProperty.type, enumValueType); const elementCount = valuesDataBytes.byteLength / elementSize; let valuesData = convertRawBufferToMetadataArray(valuesDataBytes, classProperty.type, enumValueType, elementCount); if (!valuesData) { valuesData = valuesDataBytes; } if (classProperty.array) { if (arrayOffsets) { return parseVariableLengthArrayENUM({ valuesData, numberOfElements, arrayOffsets, valuesDataBytesLength: valuesDataBytes.length, elementSize, enumEntry }); } const arrayCount = classProperty.count; if (arrayCount) { return parseFixedLengthArrayENUM(valuesData, numberOfElements, arrayCount, enumEntry); } return []; } return getEnumsArray(valuesData, 0, numberOfElements, enumEntry); } function parseVariableLengthArrayENUM(params) { const { valuesData, numberOfElements, arrayOffsets, valuesDataBytesLength, elementSize, enumEntry } = params; const attributeValueArray = []; for (let index = 0; index < numberOfElements; index++) { const arrayOffset = arrayOffsets[index]; const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index]; if (arrayByteSize + arrayOffset > valuesDataBytesLength) { break; } const typedArrayOffset = arrayOffset / elementSize; const elementCount = arrayByteSize / elementSize; const array = getEnumsArray(valuesData, typedArrayOffset, elementCount, enumEntry); attributeValueArray.push(array); } return attributeValueArray; } function parseFixedLengthArrayENUM(valuesData, numberOfElements, arrayCount, enumEntry) { const attributeValueArray = []; for (let index = 0; index < numberOfElements; index++) { const elementOffset = arrayCount * index; const array = getEnumsArray(valuesData, elementOffset, arrayCount, enumEntry); attributeValueArray.push(array); } return attributeValueArray; } function getEnumsArray(valuesData, offset, count, enumEntry) { const array = []; for (let i = 0; i < count; i++) { if (valuesData instanceof BigInt64Array || valuesData instanceof BigUint64Array) { array.push(""); } else { const value = valuesData[offset + i]; const enumObject = getEnumByValue(enumEntry, value); if (enumObject) { array.push(enumObject.name); } else { array.push(""); } } } return array; } function getEnumByValue(enumEntry, value) { for (const enumValue of enumEntry.values) { if (enumValue.value === value) { return enumValue; } } return null; } var SCHEMA_CLASS_ID_DEFAULT = "schemaClassId"; function encodeExtStructuralMetadata(scenegraph, options) { var _a, _b; const extension = scenegraph.getExtension(EXT_STRUCTURAL_METADATA_NAME); if (!extension) { return; } if (extension.propertyTables) { for (const table of extension.propertyTables) { const classId = table.class; const schemaClass = (_b = (_a = extension.schema) == null ? void 0 : _a.classes) == null ? void 0 : _b[classId]; if (table.properties && schemaClass) { encodeProperties(table, schemaClass, scenegraph); } } } } function encodeProperties(table, schemaClass, scenegraph) { for (const propertyName in table.properties) { const data = table.properties[propertyName].data; if (data) { const classProperty = schemaClass.properties[propertyName]; if (classProperty) { const tableProperty = createPropertyTableProperty(data, classProperty, scenegraph); table.properties[propertyName] = tableProperty; } } } } function createExtStructuralMetadata(scenegraph, propertyAttributes, classId = SCHEMA_CLASS_ID_DEFAULT) { let extension = scenegraph.getExtension(EXT_STRUCTURAL_METADATA_NAME); if (!extension) { extension = scenegraph.addExtension(EXT_STRUCTURAL_METADATA_NAME); } extension.schema = createSchema(propertyAttributes, classId, extension.schema); const table = createPropertyTable(propertyAttributes, classId, extension.schema); if (!extension.propertyTables) { extension.propertyTables = []; } return extension.propertyTables.push(table) - 1; } function createSchema(propertyAttributes, classId, schemaToUpdate) { const schema = schemaToUpdate ?? { id: "schema_id" }; const schemaClass = { properties: {} }; for (const attribute of propertyAttributes) { const classProperty = { type: attribute.elementType, componentType: attribute.componentType }; schemaClass.properties[attribute.name] = classProperty; } schema.classes = {}; schema.classes[classId] = schemaClass; return schema; } function createPropertyTable(propertyAttributes, classId, schema) { var _a; const table = { class: classId, count: 0 }; let count = 0; const schemaClass = (_a = schema.classes) == null ? void 0 : _a[classId]; for (const attribute of propertyAttributes) { if (count === 0) { count = attribute.values.length; } if (count !== attribute.values.length && attribute.values.length) { throw new Error("Illegal values in attributes"); } const classProperty = schemaClass == null ? void 0 : schemaClass.properties[attribute.name]; if (classProperty) { if (!table.properties) { table.properties = {}; } table.properties[attribute.name] = { values: 0, data: attribute.values }; } } table.count = count; return table; } function createPropertyTableProperty(values, classProperty, scenegraph) { const prop = { values: 0 }; if