UNPKG

@loaders.gl/draco

Version:

Framework-independent loader and writer for Draco compressed meshes and point clouds

5 lines 66.8 kB
{ "version": 3, "sources": ["../src/index.ts", "../src/lib/draco-module-loader.ts", "../src/draco-writer.ts", "../src/lib/draco-builder.ts", "../src/lib/utils/version.ts", "../src/draco-loader.ts", "../src/lib/draco-parser.ts", "../src/lib/utils/get-draco-schema.ts", "../src/draco-arrow-loader.ts"], "sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// Module constants\nexport {DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS} from './lib/draco-module-loader';\n\n// Draco data types\n\nexport type {DracoMesh, DracoLoaderData} from './lib/draco-types';\n\n// Draco Writer\n\nexport type {DracoWriterOptions} from './draco-writer';\nexport {DracoWriterWorker, DracoWriter} from './draco-writer';\n\n// Draco Loader\n\nexport type {DracoLoaderOptions} from './draco-loader';\nexport {DracoWorkerLoader, DracoLoader} from './draco-loader';\nexport {DracoArrowLoader} from './draco-arrow-loader';\n", "// Dynamic DRACO module loading inspired by THREE.DRACOLoader\n// https://github.com/mrdoob/three.js/blob/398c4f39ebdb8b23eefd4a7a5ec49ec0c96c7462/examples/jsm/loaders/DRACOLoader.js\n// by Don McCurdy / https://www.donmccurdy.com / MIT license\n\nimport {isBrowser, loadLibrary, type LoadLibraryOptions} from '@loaders.gl/worker-utils';\n\nconst DRACO_DECODER_VERSION = '1.5.6';\nconst DRACO_ENCODER_VERSION = '1.4.1';\n\nconst STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`;\n\nexport const DRACO_EXTERNAL_LIBRARIES = {\n /** The primary Draco3D encoder, javascript wrapper part */\n DECODER: 'draco_wasm_wrapper.js',\n /** The primary draco decoder, compiled web assembly part */\n DECODER_WASM: 'draco_decoder.wasm',\n /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */\n FALLBACK_DECODER: 'draco_decoder.js',\n /** Draco encoder */\n ENCODER: 'draco_encoder.js'\n};\n\nexport const DRACO_EXTERNAL_LIBRARY_URLS = {\n [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`,\n [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`,\n [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`,\n [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}`\n};\n\nlet loadDecoderPromise;\nlet loadEncoderPromise;\n\nexport async function loadDracoDecoderModule(\n options: LoadLibraryOptions = {},\n type: 'wasm' | 'js'\n) {\n const modules = options.modules || {};\n\n // Check if a bundled draco3d library has been supplied by application\n if (modules.draco3d) {\n loadDecoderPromise ||= modules.draco3d.createDecoderModule({}).then((draco) => {\n return {draco};\n });\n } else {\n // If not, dynamically load the WASM script from our CDN\n loadDecoderPromise ||= loadDracoDecoder(options, type);\n }\n return await loadDecoderPromise;\n}\n\nexport async function loadDracoEncoderModule(options: LoadLibraryOptions) {\n const modules = options.modules || {};\n\n // Check if a bundled draco3d library has been supplied by application\n if (modules.draco3d) {\n loadEncoderPromise ||= modules.draco3d.createEncoderModule({}).then((draco) => {\n return {draco};\n });\n } else {\n // If not, dynamically load the WASM script from our CDN\n loadEncoderPromise ||= loadDracoEncoder(options);\n }\n return await loadEncoderPromise;\n}\n\nfunction getLibraryExport(library: any, exportName: string): any {\n if (library && typeof library === 'object') {\n if (library.default) {\n return library.default;\n }\n if (library[exportName]) {\n return library[exportName];\n }\n }\n return library;\n}\n\n// DRACO DECODER LOADING\n/** @todo - type the options, they are inconsistent */\nasync function loadDracoDecoder(options: LoadLibraryOptions, type: 'wasm' | 'js') {\n let DracoDecoderModule;\n let wasmBinary;\n switch (type) {\n case 'js':\n DracoDecoderModule = await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER],\n 'draco',\n options,\n DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER\n );\n break;\n\n case 'wasm':\n default:\n try {\n [DracoDecoderModule, wasmBinary] = await Promise.all([\n await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER],\n 'draco',\n options,\n DRACO_EXTERNAL_LIBRARIES.DECODER\n ),\n await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM],\n 'draco',\n options,\n DRACO_EXTERNAL_LIBRARIES.DECODER_WASM\n )\n ]);\n } catch {\n DracoDecoderModule = null;\n wasmBinary = null;\n }\n }\n DracoDecoderModule = getLibraryExport(DracoDecoderModule, 'DracoDecoderModule');\n // @ts-ignore\n DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule;\n\n // In Node environments without network access, fall back to local copies in the repo.\n if (!DracoDecoderModule && !isBrowser) {\n [DracoDecoderModule, wasmBinary] = await Promise.all([\n await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER],\n 'draco',\n {...options, useLocalLibraries: true},\n DRACO_EXTERNAL_LIBRARIES.DECODER\n ),\n await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM],\n 'draco',\n {...options, useLocalLibraries: true},\n DRACO_EXTERNAL_LIBRARIES.DECODER_WASM\n )\n ]);\n DracoDecoderModule = getLibraryExport(DracoDecoderModule, 'DracoDecoderModule');\n // @ts-ignore\n DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule;\n }\n\n return await initializeDracoDecoder(DracoDecoderModule, wasmBinary);\n}\n\nfunction initializeDracoDecoder(DracoDecoderModule, wasmBinary) {\n if (typeof DracoDecoderModule !== 'function') {\n throw new Error('DracoDecoderModule could not be loaded');\n }\n\n const options: {wasmBinary?: any} = {};\n if (wasmBinary) {\n options.wasmBinary = wasmBinary;\n }\n\n return new Promise((resolve) => {\n DracoDecoderModule({\n ...options,\n onModuleLoaded: (draco) => resolve({draco}) // Module is Promise-like. Wrap in object to avoid loop.\n });\n });\n}\n\n// ENCODER\n\nasync function loadDracoEncoder(options: LoadLibraryOptions) {\n let DracoEncoderModule = await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER],\n 'draco',\n options,\n DRACO_EXTERNAL_LIBRARIES.ENCODER\n );\n DracoEncoderModule = getLibraryExport(DracoEncoderModule, 'DracoEncoderModule');\n // @ts-ignore\n DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule;\n\n // In Node environments without network access, fall back to local copies in the repo.\n if (!DracoEncoderModule && !isBrowser) {\n DracoEncoderModule = await loadLibrary(\n DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER],\n 'draco',\n {...options, useLocalLibraries: true},\n DRACO_EXTERNAL_LIBRARIES.ENCODER\n );\n DracoEncoderModule = getLibraryExport(DracoEncoderModule, 'DracoEncoderModule');\n // @ts-ignore\n DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule;\n }\n\n if (typeof DracoEncoderModule !== 'function') {\n throw new Error('DracoEncoderModule could not be loaded');\n }\n\n return new Promise((resolve) => {\n DracoEncoderModule({\n onModuleLoaded: (draco) => resolve({draco}) // Module is Promise-like. Wrap in object to avoid loop.\n });\n });\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils';\nimport {extractLoadLibraryOptions} from '@loaders.gl/worker-utils';\nimport type {DracoMesh} from './lib/draco-types';\nimport type {DracoBuildOptions} from './lib/draco-builder';\nimport DRACOBuilder from './lib/draco-builder';\nimport {loadDracoEncoderModule} from './lib/draco-module-loader';\nimport {VERSION} from './lib/utils/version';\n\n/** Writer Options for draco */\nexport type DracoWriterOptions = WriterOptions & {\n draco?: DracoBuildOptions & {\n method?: 'MESH_EDGEBREAKER_ENCODING' | 'MESH_SEQUENTIAL_ENCODING';\n speed?: [number, number];\n quantization?: Record<string, number>;\n attributeNameEntry?: string;\n };\n};\n\nconst DEFAULT_DRACO_WRITER_OPTIONS = {\n pointcloud: false, // Set to true if pointcloud (mode: 0, no indices)\n attributeNameEntry: 'name'\n // Draco Compression Parameters\n // method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults\n // speed: [5, 5], // Use draco defaults\n // quantization: { // Use draco defaults\n // POSITION: 10\n // }\n};\n\n/**\n * Browser worker doesn't work because of issue during \"draco_encoder.js\" loading.\n * Refused to execute script from 'https://raw.githubusercontent.com/google/draco/1.4.1/javascript/draco_encoder.js' because its MIME type ('') is not executable.\n */\nexport const DracoWriterWorker = {\n id: 'draco-writer',\n name: 'Draco compressed geometry writer',\n module: 'draco',\n version: VERSION,\n worker: true,\n options: {\n draco: {},\n source: null\n }\n};\n\n/**\n * Exporter for Draco3D compressed geometries\n */\nexport const DracoWriter = {\n name: 'DRACO',\n id: 'draco',\n module: 'draco',\n version: VERSION,\n extensions: ['drc'],\n mimeTypes: ['application/octet-stream'],\n options: {\n draco: DEFAULT_DRACO_WRITER_OPTIONS\n },\n encode\n} as const satisfies WriterWithEncoder<DracoMesh, unknown, DracoWriterOptions>;\n\nasync function encode(data: DracoMesh, options: DracoWriterOptions = {}): Promise<ArrayBuffer> {\n // Dynamically load draco\n const {draco} = await loadDracoEncoderModule(extractLoadLibraryOptions(options));\n const dracoBuilder = new DRACOBuilder(draco);\n\n try {\n return dracoBuilder.encodeSync(data, options.draco);\n } finally {\n dracoBuilder.destroy();\n }\n}\n", "/* eslint-disable camelcase */\n// This code is inspired by example code in the DRACO repository\nimport type {\n Draco3D,\n DracoInt8Array,\n Encoder,\n Mesh,\n MeshBuilder,\n PointCloud,\n Metadata,\n MetadataBuilder,\n draco_GeometryAttribute_Type\n} from '../draco3d/draco3d-types';\n\nimport type {TypedArray} from '@loaders.gl/schema';\nimport type {DracoMesh} from './draco-types';\n\nexport type DracoBuildOptions = {\n pointcloud?: boolean;\n metadata?: {[key: string]: string};\n attributesMetadata?: {};\n log?: any;\n\n // draco encoding options\n speed?: [number, number];\n method?: string;\n quantization?: {[attributeName: string]: number};\n};\n\n// Native Draco attribute names to GLTF attribute names.\nconst GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = {\n POSITION: 'POSITION',\n NORMAL: 'NORMAL',\n COLOR_0: 'COLOR',\n TEXCOORD_0: 'TEX_COORD'\n};\n\nconst noop = () => {};\n\nexport default class DracoBuilder {\n draco: Draco3D;\n dracoEncoder: Encoder;\n dracoMeshBuilder: MeshBuilder;\n dracoMetadataBuilder: MetadataBuilder;\n log: any;\n\n // draco - the draco decoder, either import `draco3d` or load dynamically\n constructor(draco: Draco3D) {\n this.draco = draco;\n this.dracoEncoder = new this.draco.Encoder();\n this.dracoMeshBuilder = new this.draco.MeshBuilder();\n this.dracoMetadataBuilder = new this.draco.MetadataBuilder();\n }\n\n destroy(): void {\n this.destroyEncodedObject(this.dracoMeshBuilder);\n this.destroyEncodedObject(this.dracoEncoder);\n this.destroyEncodedObject(this.dracoMetadataBuilder);\n // @ts-ignore\n this.dracoMeshBuilder = null;\n // @ts-ignore\n this.dracoEncoder = null;\n // @ts-ignore\n this.draco = null;\n }\n\n // TBD - when does this need to be called?\n destroyEncodedObject(object): void {\n if (object) {\n this.draco.destroy(object);\n }\n }\n\n /**\n * Encode mesh or point cloud\n * @param mesh =({})\n * @param options\n */\n encodeSync(mesh: DracoMesh, options: DracoBuildOptions = {}): ArrayBuffer {\n this.log = noop; // TODO\n this._setOptions(options);\n\n return options.pointcloud\n ? this._encodePointCloud(mesh, options)\n : this._encodeMesh(mesh, options);\n }\n\n // PRIVATE\n\n _getAttributesFromMesh(mesh: DracoMesh) {\n // TODO - Change the encodePointCloud interface instead?\n const attributes = {...mesh, ...mesh.attributes};\n // Fold indices into the attributes\n if (mesh.indices) {\n attributes.indices = mesh.indices;\n }\n return attributes;\n }\n\n _encodePointCloud(pointcloud: DracoMesh, options: DracoBuildOptions): ArrayBuffer {\n const dracoPointCloud = new this.draco.PointCloud();\n\n if (options.metadata) {\n this._addGeometryMetadata(dracoPointCloud, options.metadata);\n }\n\n const attributes = this._getAttributesFromMesh(pointcloud);\n\n // Build a `DracoPointCloud` from the input data\n this._createDracoPointCloud(dracoPointCloud, attributes, options);\n\n const dracoData = new this.draco.DracoInt8Array();\n\n try {\n const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(\n dracoPointCloud,\n false,\n dracoData\n );\n\n if (!(encodedLen > 0)) {\n throw new Error('Draco encoding failed.');\n }\n\n this.log(`DRACO encoded ${dracoPointCloud.num_points()} points\n with ${dracoPointCloud.num_attributes()} attributes into ${encodedLen} bytes`);\n\n return dracoInt8ArrayToArrayBuffer(dracoData);\n } finally {\n this.destroyEncodedObject(dracoData);\n this.destroyEncodedObject(dracoPointCloud);\n }\n }\n\n _encodeMesh(mesh: DracoMesh, options: DracoBuildOptions): ArrayBuffer {\n const dracoMesh = new this.draco.Mesh();\n\n if (options.metadata) {\n this._addGeometryMetadata(dracoMesh, options.metadata);\n }\n\n const attributes = this._getAttributesFromMesh(mesh);\n\n // Build a `DracoMesh` from the input data\n this._createDracoMesh(dracoMesh, attributes, options);\n\n const dracoData = new this.draco.DracoInt8Array();\n\n try {\n const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData);\n if (encodedLen <= 0) {\n throw new Error('Draco encoding failed.');\n }\n\n this.log(`DRACO encoded ${dracoMesh.num_points()} points\n with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`);\n\n return dracoInt8ArrayToArrayBuffer(dracoData);\n } finally {\n this.destroyEncodedObject(dracoData);\n this.destroyEncodedObject(dracoMesh);\n }\n }\n\n /**\n * Set encoding options.\n * @param {{speed?: any; method?: any; quantization?: any;}} options\n */\n _setOptions(options: DracoBuildOptions): void {\n if ('speed' in options) {\n // @ts-ignore\n this.dracoEncoder.SetSpeedOptions(...options.speed);\n }\n if ('method' in options) {\n const dracoMethod = this.draco[options.method || 'MESH_SEQUENTIAL_ENCODING'];\n // assert(dracoMethod)\n this.dracoEncoder.SetEncodingMethod(dracoMethod);\n }\n if ('quantization' in options) {\n for (const attribute in options.quantization) {\n const bits = options.quantization[attribute];\n const dracoPosition = this.draco[attribute];\n this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits);\n }\n }\n }\n\n /**\n * @param {Mesh} dracoMesh\n * @param {object} attributes\n * @returns {Mesh}\n */\n _createDracoMesh(dracoMesh: Mesh, attributes, options: DracoBuildOptions): Mesh {\n const optionalMetadata = options.attributesMetadata || {};\n\n try {\n const positions = this._getPositionAttribute(attributes);\n if (!positions) {\n throw new Error('positions');\n }\n const vertexCount = positions.length / 3;\n\n for (let attributeName in attributes) {\n const attribute = attributes[attributeName];\n attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;\n const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount);\n\n if (uniqueId !== -1) {\n this._addAttributeMetadata(dracoMesh, uniqueId, {\n name: attributeName,\n ...(optionalMetadata[attributeName] || {})\n });\n }\n }\n } catch (error) {\n this.destroyEncodedObject(dracoMesh);\n throw error;\n }\n\n return dracoMesh;\n }\n\n /**\n * @param {} dracoPointCloud\n * @param {object} attributes\n */\n _createDracoPointCloud(\n dracoPointCloud: PointCloud,\n attributes: object,\n options: DracoBuildOptions\n ): PointCloud {\n const optionalMetadata = options.attributesMetadata || {};\n\n try {\n const positions = this._getPositionAttribute(attributes);\n if (!positions) {\n throw new Error('positions');\n }\n const vertexCount = positions.length / 3;\n\n for (let attributeName in attributes) {\n const attribute = attributes[attributeName];\n attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;\n const uniqueId = this._addAttributeToMesh(\n dracoPointCloud,\n attributeName,\n attribute,\n vertexCount\n );\n if (uniqueId !== -1) {\n this._addAttributeMetadata(dracoPointCloud, uniqueId, {\n name: attributeName,\n ...(optionalMetadata[attributeName] || {})\n });\n }\n }\n } catch (error) {\n this.destroyEncodedObject(dracoPointCloud);\n throw error;\n }\n\n return dracoPointCloud;\n }\n\n /**\n * @param mesh\n * @param attributeName\n * @param attribute\n * @param vertexCount\n */\n _addAttributeToMesh(\n mesh: PointCloud,\n attributeName: string,\n attribute: TypedArray,\n vertexCount: number\n ): number {\n if (!ArrayBuffer.isView(attribute)) {\n return -1;\n }\n\n const type = this._getDracoAttributeType(attributeName);\n // @ts-ignore TODO/fix types\n const size = attribute.length / vertexCount;\n\n if (type === 'indices') {\n // @ts-ignore TODO/fix types\n const numFaces = attribute.length / 3;\n this.log(`Adding attribute ${attributeName}, size ${numFaces}`);\n\n // @ts-ignore assumes mesh is a Mesh, not a point cloud\n this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute);\n return -1;\n }\n\n this.log(`Adding attribute ${attributeName}, size ${size}`);\n\n const builder = this.dracoMeshBuilder;\n const {buffer} = attribute;\n\n switch (attribute.constructor) {\n case Int8Array:\n return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer));\n\n case Int16Array:\n return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer));\n\n case Int32Array:\n return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer));\n case Uint8Array:\n case Uint8ClampedArray:\n return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer));\n\n case Uint16Array:\n return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer));\n\n case Uint32Array:\n return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer));\n\n case Float32Array:\n return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer));\n\n default:\n // eslint-disable-next-line no-console\n console.warn('Unsupported attribute type', attribute);\n return -1;\n }\n\n // case Float64Array:\n // Add attribute does not seem to be exposed\n // return builder.AddAttribute(mesh, type, vertexCount, size, new Float32Array(buffer));\n }\n\n /**\n * DRACO can compress attributes of know type better\n * TODO - expose an attribute type map?\n * @param attributeName\n */\n _getDracoAttributeType(attributeName: string): draco_GeometryAttribute_Type | 'indices' {\n switch (attributeName.toLowerCase()) {\n case 'indices':\n return 'indices';\n case 'position':\n case 'positions':\n case 'vertices':\n return this.draco.POSITION;\n case 'normal':\n case 'normals':\n return this.draco.NORMAL;\n case 'color':\n case 'colors':\n return this.draco.COLOR;\n case 'texcoord':\n case 'texcoords':\n return this.draco.TEX_COORD;\n default:\n return this.draco.GENERIC;\n }\n }\n\n _getPositionAttribute(attributes) {\n for (const attributeName in attributes) {\n const attribute = attributes[attributeName];\n const dracoType = this._getDracoAttributeType(attributeName);\n if (dracoType === this.draco.POSITION) {\n return attribute;\n }\n }\n return null;\n }\n\n /**\n * Add metadata for the geometry.\n * @param dracoGeometry - WASM Draco Object\n * @param metadata\n */\n _addGeometryMetadata(dracoGeometry: PointCloud, metadata: {[key: string]: string}) {\n const dracoMetadata = new this.draco.Metadata();\n this._populateDracoMetadata(dracoMetadata, metadata);\n this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata);\n }\n\n /**\n * Add metadata for an attribute to geometry.\n * @param dracoGeometry - WASM Draco Object\n * @param uniqueAttributeId\n * @param metadata\n */\n _addAttributeMetadata(\n dracoGeometry: PointCloud,\n uniqueAttributeId: number,\n metadata: Map<string, string> | {[key: string]: string}\n ) {\n // Note: Draco JS IDL doesn't seem to expose draco.AttributeMetadata, however it seems to\n // create such objects automatically from draco.Metadata object.\n const dracoAttributeMetadata = new this.draco.Metadata();\n this._populateDracoMetadata(dracoAttributeMetadata, metadata);\n // Draco3d doc note: Directly add attribute metadata to geometry.\n // You can do this without explicitly adding |GeometryMetadata| to mesh.\n this.dracoMeshBuilder.SetMetadataForAttribute(\n dracoGeometry,\n uniqueAttributeId,\n dracoAttributeMetadata\n );\n }\n\n /**\n * Add contents of object or map to a WASM Draco Metadata Object\n * @param dracoMetadata - WASM Draco Object\n * @param metadata\n */\n _populateDracoMetadata(\n dracoMetadata: Metadata,\n metadata: Map<string, string> | {[key: string]: string}\n ) {\n for (const [key, value] of getEntries(metadata)) {\n switch (typeof value) {\n case 'number':\n if (Math.trunc(value) === value) {\n this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value);\n } else {\n this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value);\n }\n break;\n case 'object':\n if (value instanceof Int32Array) {\n this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length);\n }\n break;\n case 'string':\n default:\n this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value);\n }\n }\n }\n}\n\n// HELPER FUNCTIONS\n\n/**\n * Copy encoded data to buffer\n * @param dracoData\n */\nfunction dracoInt8ArrayToArrayBuffer(dracoData: DracoInt8Array) {\n const byteLength = dracoData.size();\n const outputBuffer = new ArrayBuffer(byteLength);\n const outputData = new Int8Array(outputBuffer);\n for (let i = 0; i < byteLength; ++i) {\n outputData[i] = dracoData.GetValue(i);\n }\n return outputBuffer;\n}\n\n/** Enable iteration over either an object or a map */\nfunction getEntries(container) {\n const hasEntriesFunc = container.entries && !container.hasOwnProperty('entries');\n return hasEntriesFunc ? container.entries() : Object.entries(container);\n}\n", "// Version constant cannot be imported, it needs to correspond to the build version of **this** module.\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nexport const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {Loader, LoaderWithParser, StrictLoaderOptions} from '@loaders.gl/loader-utils';\nimport {extractLoadLibraryOptions} from '@loaders.gl/worker-utils';\nimport type {DracoMesh} from './lib/draco-types';\nimport type {DracoParseOptions} from './lib/draco-parser';\nimport {VERSION} from './lib/utils/version';\nimport DracoParser from './lib/draco-parser';\nimport {loadDracoDecoderModule} from './lib/draco-module-loader';\n\nexport type DracoLoaderOptions = StrictLoaderOptions & {\n draco?: DracoParseOptions & {\n /** @deprecated WASM decoding is faster but JS is more backwards compatible */\n decoderType?: 'wasm' | 'js';\n /** Override the URL to the worker bundle (by default loads from unpkg.com) */\n workerUrl?: string;\n };\n};\n\n/**\n * Worker loader for Draco3D compressed geometries\n */\nexport const DracoWorkerLoader = {\n dataType: null as unknown as DracoMesh,\n batchType: null as never,\n name: 'Draco',\n id: 'draco',\n module: 'draco',\n // shapes: ['mesh'],\n version: VERSION,\n worker: true,\n extensions: ['drc'],\n mimeTypes: ['application/octet-stream'],\n binary: true,\n tests: ['DRACO'],\n options: {\n draco: {\n decoderType: typeof WebAssembly === 'object' ? 'wasm' : 'js', // 'js' for IE11\n extraAttributes: {},\n attributeNameEntry: undefined\n }\n }\n} as const satisfies Loader<DracoMesh, never, DracoLoaderOptions>;\n\n/**\n * Loader for Draco3D compressed geometries\n */\nexport const DracoLoader = {\n ...DracoWorkerLoader,\n parse\n} as const satisfies LoaderWithParser<DracoMesh, never, DracoLoaderOptions>;\n\nasync function parse(arrayBuffer: ArrayBuffer, options?: DracoLoaderOptions): Promise<DracoMesh> {\n const {draco} = await loadDracoDecoderModule(\n extractLoadLibraryOptions(options),\n options?.draco?.decoderType || 'wasm'\n );\n const dracoParser = new DracoParser(draco);\n try {\n return dracoParser.parseSync(arrayBuffer, options?.draco);\n } finally {\n dracoParser.destroy();\n }\n}\n", "/* eslint-disable camelcase */\n\nimport type {TypedArray, MeshAttribute, MeshGeometry} from '@loaders.gl/schema';\n\n// Draco types (input)\nimport type {\n Draco3D,\n Decoder,\n Mesh,\n PointCloud,\n PointAttribute,\n Metadata,\n MetadataQuerier,\n DracoInt32Array,\n draco_DataType\n} from '../draco3d/draco3d-types';\n\n// Parsed data types (output)\nimport type {\n DracoMesh,\n DracoLoaderData,\n DracoAttribute,\n DracoMetadataEntry,\n DracoQuantizationTransform,\n DracoOctahedronTransform\n} from './draco-types';\n\nimport {getMeshBoundingBox} from '@loaders.gl/schema-utils';\nimport {getDracoSchema} from './utils/get-draco-schema';\n\n/** Options to control draco parsing */\nexport type DracoParseOptions = {\n /** How triangle indices should be generated (mesh only) */\n topology?: 'triangle-list' | 'triangle-strip';\n /** Specify which attribute metadata entry stores the attribute name */\n attributeNameEntry?: string;\n /** Names and ids of extra attributes to include in the output */\n extraAttributes?: {[uniqueId: string]: number};\n /** Skip transforms specific quantized attributes */\n quantizedAttributes?: ('POSITION' | 'NORMAL' | 'COLOR' | 'TEX_COORD' | 'GENERIC')[];\n /** Skip transforms specific octahedron encoded attributes */\n octahedronAttributes?: ('POSITION' | 'NORMAL' | 'COLOR' | 'TEX_COORD' | 'GENERIC')[];\n};\n\n// @ts-ignore\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst GEOMETRY_TYPE = {\n TRIANGULAR_MESH: 0,\n POINT_CLOUD: 1\n};\n\n// Native Draco attribute names to GLTF attribute names.\nconst DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = {\n POSITION: 'POSITION',\n NORMAL: 'NORMAL',\n COLOR: 'COLOR_0',\n TEX_COORD: 'TEXCOORD_0'\n};\n\nconst DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = {\n 1: Int8Array,\n 2: Uint8Array,\n 3: Int16Array,\n 4: Uint16Array,\n 5: Int32Array,\n 6: Uint32Array,\n // 7: BigInt64Array,\n // 8: BigUint64Array,\n 9: Float32Array\n // 10: Float64Array\n // 11: BOOL - What array type do we use for this?\n};\n\nconst INDEX_ITEM_SIZE = 4;\n\nexport default class DracoParser {\n draco: Draco3D;\n decoder: Decoder;\n metadataQuerier: MetadataQuerier;\n\n // draco - the draco decoder, either import `draco3d` or load dynamically\n constructor(draco: Draco3D) {\n this.draco = draco;\n this.decoder = new this.draco.Decoder();\n this.metadataQuerier = new this.draco.MetadataQuerier();\n }\n\n /**\n * Destroy draco resources\n */\n destroy(): void {\n this.draco.destroy(this.decoder);\n this.draco.destroy(this.metadataQuerier);\n }\n\n /**\n * NOTE: caller must call `destroyGeometry` on the return value after using it\n * @param arrayBuffer\n * @param options\n */\n parseSync(arrayBuffer: ArrayBuffer, options: DracoParseOptions = {}): DracoMesh {\n const buffer = new this.draco.DecoderBuffer();\n buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength);\n\n this._disableAttributeTransforms(options);\n\n const geometry_type = this.decoder.GetEncodedGeometryType(buffer);\n const dracoGeometry =\n geometry_type === this.draco.TRIANGULAR_MESH\n ? new this.draco.Mesh()\n : new this.draco.PointCloud();\n\n try {\n let dracoStatus;\n switch (geometry_type) {\n case this.draco.TRIANGULAR_MESH:\n dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry as Mesh);\n break;\n\n case this.draco.POINT_CLOUD:\n dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry);\n break;\n\n default:\n throw new Error('DRACO: Unknown geometry type.');\n }\n\n if (!dracoStatus.ok() || !dracoGeometry.ptr) {\n const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`;\n // console.error(message);\n throw new Error(message);\n }\n\n const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options);\n\n const geometry = this._getMeshData(dracoGeometry, loaderData, options);\n\n const boundingBox = getMeshBoundingBox(geometry.attributes);\n\n const schema = getDracoSchema(geometry.attributes, loaderData, geometry.indices);\n\n const data: DracoMesh = {\n loader: 'draco',\n loaderData,\n header: {\n vertexCount: dracoGeometry.num_points(),\n boundingBox\n },\n ...geometry,\n schema\n };\n return data;\n } finally {\n this.draco.destroy(buffer);\n if (dracoGeometry) {\n this.draco.destroy(dracoGeometry);\n }\n }\n }\n\n // Draco specific \"loader data\"\n\n /**\n * Extract\n * @param dracoGeometry\n * @param geometry_type\n * @param options\n * @returns\n */\n _getDracoLoaderData(\n dracoGeometry: Mesh | PointCloud,\n geometry_type,\n options: DracoParseOptions\n ): DracoLoaderData {\n const metadata = this._getTopLevelMetadata(dracoGeometry);\n const attributes = this._getDracoAttributes(dracoGeometry, options);\n\n return {\n geometry_type,\n num_attributes: dracoGeometry.num_attributes(),\n num_points: dracoGeometry.num_points(),\n num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0,\n metadata,\n attributes\n };\n }\n\n /**\n * Extract all draco provided information and metadata for each attribute\n * @param dracoGeometry\n * @param options\n * @returns\n */\n _getDracoAttributes(\n dracoGeometry: Mesh | PointCloud,\n options: DracoParseOptions\n ): {[unique_id: number]: DracoAttribute} {\n const dracoAttributes: {[unique_id: number]: DracoAttribute} = {};\n\n for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {\n // Note: Draco docs do not seem clear on `GetAttribute` ids just being a zero-based index,\n // but it does seems to work this way\n const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);\n\n const metadata = this._getAttributeMetadata(dracoGeometry, attributeId);\n\n dracoAttributes[dracoAttribute.unique_id()] = {\n unique_id: dracoAttribute.unique_id(),\n attribute_type: dracoAttribute.attribute_type(),\n data_type: dracoAttribute.data_type(),\n num_components: dracoAttribute.num_components(),\n\n byte_offset: dracoAttribute.byte_offset(),\n byte_stride: dracoAttribute.byte_stride(),\n normalized: dracoAttribute.normalized(),\n attribute_index: attributeId,\n\n metadata\n };\n\n // Add transformation parameters for any attributes app wants untransformed\n const quantization = this._getQuantizationTransform(dracoAttribute, options);\n if (quantization) {\n dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization;\n }\n\n const octahedron = this._getOctahedronTransform(dracoAttribute, options);\n if (octahedron) {\n dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron;\n }\n }\n\n return dracoAttributes;\n }\n\n /**\n * Get standard loaders.gl mesh category data\n * Extracts the geometry from draco\n * @param dracoGeometry\n * @param options\n */\n _getMeshData(\n dracoGeometry: Mesh | PointCloud,\n loaderData: DracoLoaderData,\n options: DracoParseOptions\n ): MeshGeometry {\n const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options);\n\n const positionAttribute = attributes.POSITION;\n if (!positionAttribute) {\n throw new Error('DRACO: No position attribute found.');\n }\n\n // For meshes, we need indices to define the faces.\n if (dracoGeometry instanceof this.draco.Mesh) {\n switch (options.topology) {\n case 'triangle-strip':\n return {\n topology: 'triangle-strip',\n // TODO - mode is wrong?\n mode: 4, // GL.TRIANGLES\n attributes,\n indices: {\n value: this._getTriangleStripIndices(dracoGeometry),\n size: 1\n }\n };\n case 'triangle-list':\n default:\n return {\n topology: 'triangle-list',\n // TODO - mode is wrong?\n mode: 5, // GL.TRIANGLE_STRIP\n attributes,\n indices: {\n value: this._getTriangleListIndices(dracoGeometry),\n size: 1\n }\n };\n }\n }\n\n // PointCloud - must come last as Mesh inherits from PointCloud\n return {\n topology: 'point-list',\n mode: 0, // GL.POINTS\n attributes\n };\n }\n\n _getMeshAttributes(\n loaderData: DracoLoaderData,\n dracoGeometry: Mesh | PointCloud,\n options: DracoParseOptions\n ): {[attributeName: string]: MeshAttribute} {\n const attributes: {[key: string]: MeshAttribute} = {};\n\n for (const loaderAttribute of Object.values(loaderData.attributes)) {\n const attributeName = this._deduceAttributeName(loaderAttribute, options);\n loaderAttribute.name = attributeName;\n const values = this._getAttributeValues(dracoGeometry, loaderAttribute);\n if (values) {\n const {value, size} = values;\n attributes[attributeName] = {\n value,\n size,\n byteOffset: loaderAttribute.byte_offset,\n byteStride: loaderAttribute.byte_stride,\n normalized: loaderAttribute.normalized\n };\n }\n }\n\n return attributes;\n }\n\n // MESH INDICES EXTRACTION\n\n /**\n * For meshes, we need indices to define the faces.\n * @param dracoGeometry\n */\n _getTriangleListIndices(dracoGeometry: Mesh) {\n // Example on how to retrieve mesh and attributes.\n const numFaces = dracoGeometry.num_faces();\n const numIndices = numFaces * 3;\n const byteLength = numIndices * INDEX_ITEM_SIZE;\n\n const ptr = this.draco._malloc(byteLength);\n try {\n this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);\n return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice();\n } finally {\n this.draco._free(ptr);\n }\n }\n\n /**\n * For meshes, we need indices to define the faces.\n * @param dracoGeometry\n */\n _getTriangleStripIndices(dracoGeometry: Mesh) {\n const dracoArray = new this.draco.DracoInt32Array();\n try {\n /* const numStrips = */ this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray);\n return getUint32Array(dracoArray);\n } finally {\n this.draco.destroy(dracoArray);\n }\n }\n\n /**\n *\n * @param dracoGeometry\n * @param dracoAttribute\n * @param attributeName\n */\n _getAttributeValues(\n dracoGeometry: Mesh | PointCloud,\n attribute: DracoAttribute\n ): {value: TypedArray; size: number} | null {\n const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type];\n if (!TypedArrayCtor) {\n // eslint-disable-next-line no-console\n console.warn(`DRACO: Unsupported attribute type ${attribute.data_type}`);\n return null;\n }\n const numComponents = attribute.num_components;\n const numPoints = dracoGeometry.num_points();\n const numValues = numPoints * numComponents;\n\n const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT;\n const dataType = getDracoDataType(this.draco, TypedArrayCtor);\n\n let value: TypedArray;\n\n const ptr = this.draco._malloc(byteLength);\n try {\n const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index);\n this.decoder.GetAttributeDataArrayForAllPoints(\n dracoGeometry,\n dracoAttribute,\n dataType,\n byteLength,\n ptr\n );\n value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice();\n } finally {\n this.draco._free(ptr);\n }\n\n return {value, size: numComponents};\n }\n\n // Attribute names\n\n /** \n * DRACO does not store attribute names - We need to deduce an attribute name\n * for each attribute\n _getAttributeNames(\n dracoGeometry: Mesh | PointCloud,\n options: DracoParseOptions\n ): {[unique_id: number]: string} {\n const attributeNames: {[unique_id: number]: string} = {};\n for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {\n const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);\n const attributeName = this._deduceAttributeName(dracoAttribute, options);\n attributeNames[attributeName] = attributeName;\n }\n return attributeNames;\n }\n */\n\n /**\n * Deduce an attribute name.\n * @note DRACO does not save attribute names, just general type (POSITION, COLOR)\n * to help optimize compression. We generate GLTF compatible names for the Draco-recognized\n * types\n * @param attributeData\n */\n _deduceAttributeName(attribute: DracoAttribute, options: DracoParseOptions): string {\n // Deduce name based on application provided map\n const uniqueId = attribute.unique_id;\n for (const [attributeName, attributeUniqueId] of Object.entries(\n options.extraAttributes || {}\n )) {\n if (attributeUniqueId === uniqueId) {\n return attributeName;\n }\n }\n\n // Deduce name based on attribute type\n const thisAttributeType = attribute.attribute_type;\n for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) {\n const attributeType = this.draco[dracoAttributeConstant];\n if (attributeType === thisAttributeType) {\n // TODO - Return unique names if there multiple attributes per type\n // (e.g. multiple TEX_COORDS or COLORS)\n return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant];\n }\n }\n\n // Look up in metadata\n // TODO - shouldn't this have priority?\n const entryName = options.attributeNameEntry || 'name';\n if (attribute.metadata[entryName]) {\n return attribute.metadata[entryName].string;\n }\n\n // Attribute of \"GENERIC\" type, we need to assign some name\n return `CUSTOM_ATTRIBUTE_${uniqueId}`;\n }\n\n // METADATA EXTRACTION\n\n /** Get top level metadata */\n _getTopLevelMetadata(dracoGeometry: Mesh | PointCloud) {\n const dracoMetadata = this.decoder.GetMetadata(dracoGeometry);\n return this._getDracoMetadata(dracoMetadata);\n }\n\n /** Get per attribute metadata */\n _getAttributeMetadata(dracoGeometry: Mesh | PointCloud, attributeId: number) {\n const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId);\n return this._getDracoMetadata(dracoMetadata);\n }\n\n /**\n * Extract metadata field values\n * @param dracoMetadata\n * @returns\n */\n _getDracoMetadata(dracoMetadata: Metadata): {[entry: string]: DracoMetadataEntry} {\n // The not so wonderful world of undocumented Draco APIs :(\n if (!dracoMetadata || !dracoMetadata.ptr) {\n return {};\n }\n const result = {};\n const numEntries = this.metadataQuerier.NumEntries(dracoMetadata);\n for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) {\n const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex);\n result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName);\n }\n return result;\n }\n\n /**\n * Extracts possible values for one metadata entry by name\n * @param dracoMetadata\n * @param entryName\n */\n _getDracoMetadataField(dracoMetadata: Metadata, entryName: string): DracoMetadataEntry {\n const dracoArray = new this.draco.DracoInt32Array();\n try {\n // Draco metadata fields can hold int32 arrays\n this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray);\n const intArray = getInt32Array(dracoArray);\n return {\n int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName),\n string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName),\n double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName),\n intArray\n };\n } finally {\n this.draco.destroy(dracoArray);\n }\n }\n\n // QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION)\n\n /** Skip transforms for specific attribute types */\n _disableAttributeTransforms(options: DracoParseOptions) {\n const {quantizedAttributes = [], octahedronAttributes = []} = options;\n const skipAttributes = [...quantizedAttributes, ...octahedronAttributes];\n for (const dracoAttributeName of skipAttributes) {\n this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]);\n }\n }\n\n /**\n * Extract (and apply?) Position Transform\n * @todo not used\n */\n _getQuantizationTransform(\n dracoAttribute: PointAttribute,\n options: DracoParseOptions\n ): DracoQuantizationTransform | null {\n const {quantizedAttributes = []} = options;\n const attribute_type = dracoAttribute.attribute_type();\n const skip = quantizedAttributes.map((type) => this.decoder[type]).includes(attribute_type);\n if (skip) {\n const transform = new this.draco.AttributeQuantizationTransform();\n try {\n if (transform.InitFromAttribute(dracoAttribute)) {\n return {\n quantization_bits: transform.quantization_bits(),\n range: transform.range(),\n min_values: new Float32Array([1, 2, 3]).map((i) => transform.min_value(i))\n };\n }\n } finally {\n this.draco.destroy(transform);\n }\n }\n return null;\n }\n\n _getOctahedronTransform(\n dracoAttribute: PointAttribute,\n options: DracoParseOptions\n ): DracoOctahedronTransform | null {\n const {octahedronAttributes = []} = options;\n const attribute_type = dracoAttribute.attribute_type();\n const octahedron = octahedronAttributes\n .map((type) => this.decoder[type])\n .includes(attribute_type);\n if (octahedron) {\n const transform = new this.draco.AttributeQuantizationTransform();\n try {\n if (transform.InitFromAttribute(dracoAttribute)) {\n return {\n quantization_bits: transform.quantization_bits()\n };\n }\n } finally {\n this.draco.destroy(transform);\n }\n }\n return null;\n }\n\n // HELPERS\n}\n\n/**\n * Get draco specific data type by TypedArray constructor type\n * @param attributeType\n * @returns draco specific data type\n */\nfunction getDracoDataType(draco: Draco3D, attributeType: any): draco_DataType {\n switch (attributeType) {\n case Float32Array:\n return draco.DT_FLOAT32;\n case Int8Array:\n return draco.DT_INT8;\n case Int16Array:\n return draco.DT_INT16;\n case Int32Array:\n return draco.DT_INT32;\n case Uint8Array:\n return draco.DT_UINT8;\n case Uint16Array:\n return draco.DT_UINT16;\n case Uint32Array:\n return draco.DT_UINT32;\n default:\n return draco.DT_INVALID;\n }\n}\n\n/**\n * Copy a Draco int32 array into a JS typed array\n */\nfunction getInt32Array(dracoArray: DracoInt32Array): Int32Array {\n const numValues = dracoArray.size();\n const intArray = new Int32Array(numValues);\n for (let i = 0; i < numValues; i++) {\n intArray[i] = dracoArray.GetValue(i);\n }\n return intArray;\n}\n\n/**\n * Copy a Draco int32 array into a JS typed array\n */\nfunction getUint32Array(dracoArray: DracoInt32Array): Int32Array {\n const numValues = dracoArray.size();\n const intArray = new Int32Array(numValues);\n for (let i = 0; i < numValues; i++) {\n intArray[i] = dracoArray.GetValue(i);\n }\n return intArray;\n}\n", "import {MeshAttribute, Schema, Field} from '@loaders.gl/schema';\nimport {deduceMeshField} from '@loaders.gl/schema-utils';\nimport type {DracoAttribute, DracoLoaderData, DracoMetadataEntry} from '../draco-types';\n\n/** Extract an arrow-like schema from a Draco mesh */\nexport function getDracoSchema(\n attributes: {[attributeName: string]: MeshAttribute},\n loaderData: DracoLoaderData,\n indices?: MeshAttribute\n): Schema {\n const metadata = makeMetadata(loaderData.metadata);\n const fields: Field[] = [];\n const namedLoaderDataAttributes = transformAttributesLoaderData(loaderData.attributes);\n for (const attributeName in attributes) {\n const attribute = attributes[attributeName];\n const field = getArrowFieldFromAttribute(\n attributeName,\n attribute,\n namedLoaderDataAttributes[attributeName]\n );\n fields.push(field);\n }\n if (indices) {\n const indicesField = getArrowFieldFromAttribute('indices', indices);\n fields.push(indicesField);\n }\n return {fields, metadata};\n}\n\nfunction transformAttributesLoaderData(loaderData: {[key: number]: DracoAttribute}): {\n [attributeName: string]: DracoAttribute;\n} {\n const result: {[attributeName: string]: DracoAttribute} = {};\n for (const key in loaderData) {\n const dracoAttribute = loaderData[key];\n result[dracoAttribute.name || 'undefined'] = dracoAttribute;\n }\n return result;\n}\n\nfunction getArrowFieldFromAttribute(\n attributeName: string,\n attribute: MeshAttribute,\n loaderData?: DracoAttribute\n): Field {\n const metadataMap = loaderData ? makeMetadata(loaderData.metadata) : undefined;\n const field = deduceMeshField(attributeName, attribute, metadataMap);\n return field;\n}\n\nfunction makeMetadata(metadata: {[key: string]: DracoMetadataEntry}): Record<string, string> {\n Object.entries(metadata);\n const serializedMetadata: Record<string, string> = {};\n for (const key in metadata) {\n serializedMetadata[`${key}.string`] = JSON.stringify(metadata[key]);\n }\n return serializedMetadata;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {ArrowTable} from '@loaders.gl/schema';\nimport type {LoaderWithParser} from '@loaders.gl/loader-utils';\nimport type {DracoLoaderOptions} from './draco-loader';\nimport {DracoLoader} from './draco-loader';\nimport {convertMeshToTable} from '@loaders.gl/schema-utils';\n\n/**\n * Loader for Draco3D compressed geometries\n */\nexport const DracoArrowLoader = {\n ...DracoLoader,\n dataType: null as unknown as ArrowTable,\n worker: false,\n parse\n} as const satisfies LoaderWithParser<ArrowTable, never, DracoLoaderOptions>;\n\nasync function parse(arrayBuffer: ArrayBuffer, options?: DracoLoaderOptions): Promise<ArrowTable> {\n const mesh = await DracoLoader.parse(arrayBuffer, options);\n const arrowTable = convertMeshToTable(mesh, 'arrow-table');\n return arrowTable;\n}\n"], "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;ACIA,0BAA8D;AAE9D,IAAM,wBAAwB;AAC9B,IAAM,wBAAwB;AAE9B,IAAM,qBAAqB,oDAAoD;AAExE,IAAM,2BAA2B;;EAEtC,SAAS;;EAET,cAAc;;EAEd,kBAAkB;;EAElB,SAAS;;AAGJ,IAAM,8BAA8B;EACzC,CAAC,yBAAyB,OAAO,GAAG,GAAG,sBAAsB,yBAAyB;EACtF,CAAC,yBAAyB,YAAY,GAAG,GAAG,sBAAsB,yBAAyB;EAC3F,CAAC,yBAAyB,gBAAgB,GAAG,GAAG,sBAAsB,yBAAyB;EAC/F,CAAC,yBAAyB,OAAO,GAAG,kDAAkD,oCAAoC,yBAAyB;;AAGrJ,IAAI;AACJ,IA