@loaders.gl/draco
Advanced tools
Comparing version 4.2.0-alpha.4 to 4.2.0-alpha.5
@@ -5,4 +5,4 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
else if (typeof define === 'function' && define.amd) define([], factory); | ||
else if (typeof exports === 'object') exports['loader'] = factory(); | ||
else root['loader'] = factory();})(globalThis, function () { | ||
else if (typeof exports === 'object') exports['loaders'] = factory(); | ||
else root['loaders'] = factory();})(globalThis, function () { | ||
"use strict"; | ||
@@ -31,2 +31,3 @@ var __exports__ = (() => { | ||
}; | ||
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
@@ -42,2 +43,9 @@ // If the importer is in node compatibility mode or this is not an ESM | ||
// external-global-plugin:@loaders.gl/core | ||
var require_core = __commonJS({ | ||
"external-global-plugin:@loaders.gl/core"(exports, module) { | ||
module.exports = globalThis.loaders; | ||
} | ||
}); | ||
// (disabled):../worker-utils/src/lib/node/require-utils.node | ||
@@ -50,5 +58,5 @@ var require_require_utils = __commonJS({ | ||
// src/index.ts | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
// bundle.ts | ||
var bundle_exports = {}; | ||
__export(bundle_exports, { | ||
DRACO_EXTERNAL_LIBRARIES: () => DRACO_EXTERNAL_LIBRARIES, | ||
@@ -61,5 +69,6 @@ DRACO_EXTERNAL_LIBRARY_URLS: () => DRACO_EXTERNAL_LIBRARY_URLS, | ||
}); | ||
__reExport(bundle_exports, __toESM(require_core(), 1)); | ||
// src/lib/utils/version.ts | ||
var VERSION = true ? "4.2.0-alpha.4" : "latest"; | ||
var VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest"; | ||
@@ -71,2 +80,3 @@ // src/draco-loader.ts | ||
module: "draco", | ||
// shapes: ['mesh'], | ||
version: VERSION, | ||
@@ -81,2 +91,3 @@ worker: true, | ||
decoderType: typeof WebAssembly === "object" ? "wasm" : "js", | ||
// 'js' for IE11 | ||
libraryPath: "libs/", | ||
@@ -135,3 +146,6 @@ extraAttributes: {}, | ||
} | ||
return [[minX, minY, minZ], [maxX, maxY, maxZ]]; | ||
return [ | ||
[minX, minY, minZ], | ||
[maxX, maxY, maxZ] | ||
]; | ||
} | ||
@@ -145,10 +159,3 @@ | ||
name, | ||
type: { | ||
type: "fixed-size-list", | ||
listSize: attribute.size, | ||
children: [{ | ||
name: "value", | ||
type | ||
}] | ||
}, | ||
type: { type: "fixed-size-list", listSize: attribute.size, children: [{ name: "value", type }] }, | ||
nullable: false, | ||
@@ -179,3 +186,7 @@ metadata | ||
const attribute = attributes[attributeName]; | ||
const field = getArrowFieldFromAttribute(attributeName, attribute, namedLoaderDataAttributes[attributeName]); | ||
const field = getArrowFieldFromAttribute( | ||
attributeName, | ||
attribute, | ||
namedLoaderDataAttributes[attributeName] | ||
); | ||
fields.push(field); | ||
@@ -187,6 +198,3 @@ } | ||
} | ||
return { | ||
fields, | ||
metadata | ||
}; | ||
return { fields, metadata }; | ||
} | ||
@@ -233,2 +241,3 @@ function transformAttributesLoaderData(loaderData) { | ||
var DracoParser = class { | ||
// draco - the draco decoder, either import `draco3d` or load dynamically | ||
constructor(draco) { | ||
@@ -239,2 +248,5 @@ this.draco = draco; | ||
} | ||
/** | ||
* Destroy draco resources | ||
*/ | ||
destroy() { | ||
@@ -244,2 +256,7 @@ this.draco.destroy(this.decoder); | ||
} | ||
/** | ||
* NOTE: caller must call `destroyGeometry` on the return value after using it | ||
* @param arrayBuffer | ||
* @param options | ||
*/ | ||
parseSync(arrayBuffer, options = {}) { | ||
@@ -289,2 +306,10 @@ const buffer = new this.draco.DecoderBuffer(); | ||
} | ||
// Draco specific "loader data" | ||
/** | ||
* Extract | ||
* @param dracoGeometry | ||
* @param geometry_type | ||
* @param options | ||
* @returns | ||
*/ | ||
_getDracoLoaderData(dracoGeometry, geometry_type, options) { | ||
@@ -302,2 +327,8 @@ const metadata = this._getTopLevelMetadata(dracoGeometry); | ||
} | ||
/** | ||
* Extract all draco provided information and metadata for each attribute | ||
* @param dracoGeometry | ||
* @param options | ||
* @returns | ||
*/ | ||
_getDracoAttributes(dracoGeometry, options) { | ||
@@ -330,2 +361,8 @@ const dracoAttributes = {}; | ||
} | ||
/** | ||
* Get standard loaders.gl mesh category data | ||
* Extracts the geometry from draco | ||
* @param dracoGeometry | ||
* @param options | ||
*/ | ||
_getMeshData(dracoGeometry, loaderData, options) { | ||
@@ -343,2 +380,3 @@ const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options); | ||
mode: 4, | ||
// GL.TRIANGLES | ||
attributes, | ||
@@ -355,2 +393,3 @@ indices: { | ||
mode: 5, | ||
// GL.TRIANGLE_STRIP | ||
attributes, | ||
@@ -367,2 +406,3 @@ indices: { | ||
mode: 0, | ||
// GL.POINTS | ||
attributes | ||
@@ -376,6 +416,3 @@ }; | ||
loaderAttribute.name = attributeName; | ||
const { | ||
value, | ||
size | ||
} = this._getAttributeValues(dracoGeometry, loaderAttribute); | ||
const { value, size } = this._getAttributeValues(dracoGeometry, loaderAttribute); | ||
attributes[attributeName] = { | ||
@@ -391,2 +428,7 @@ value, | ||
} | ||
// MESH INDICES EXTRACTION | ||
/** | ||
* For meshes, we need indices to define the faces. | ||
* @param dracoGeometry | ||
*/ | ||
_getTriangleListIndices(dracoGeometry) { | ||
@@ -404,2 +446,6 @@ const numFaces = dracoGeometry.num_faces(); | ||
} | ||
/** | ||
* For meshes, we need indices to define the faces. | ||
* @param dracoGeometry | ||
*/ | ||
_getTriangleStripIndices(dracoGeometry) { | ||
@@ -414,2 +460,8 @@ const dracoArray = new this.draco.DracoInt32Array(); | ||
} | ||
/** | ||
* | ||
* @param dracoGeometry | ||
* @param dracoAttribute | ||
* @param attributeName | ||
*/ | ||
_getAttributeValues(dracoGeometry, attribute) { | ||
@@ -426,3 +478,9 @@ const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type]; | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index); | ||
this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr); | ||
this.decoder.GetAttributeDataArrayForAllPoints( | ||
dracoGeometry, | ||
dracoAttribute, | ||
dataType, | ||
byteLength, | ||
ptr | ||
); | ||
value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice(); | ||
@@ -432,10 +490,33 @@ } finally { | ||
} | ||
return { | ||
value, | ||
size: numComponents | ||
}; | ||
return { value, size: numComponents }; | ||
} | ||
// Attribute names | ||
/** | ||
* DRACO does not store attribute names - We need to deduce an attribute name | ||
* for each attribute | ||
_getAttributeNames( | ||
dracoGeometry: Mesh | PointCloud, | ||
options: DracoParseOptions | ||
): {[unique_id: number]: string} { | ||
const attributeNames: {[unique_id: number]: string} = {}; | ||
for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); | ||
const attributeName = this._deduceAttributeName(dracoAttribute, options); | ||
attributeNames[attributeName] = attributeName; | ||
} | ||
return attributeNames; | ||
} | ||
*/ | ||
/** | ||
* Deduce an attribute name. | ||
* @note DRACO does not save attribute names, just general type (POSITION, COLOR) | ||
* to help optimize compression. We generate GLTF compatible names for the Draco-recognized | ||
* types | ||
* @param attributeData | ||
*/ | ||
_deduceAttributeName(attribute, options) { | ||
const uniqueId = attribute.unique_id; | ||
for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) { | ||
for (const [attributeName, attributeUniqueId] of Object.entries( | ||
options.extraAttributes || {} | ||
)) { | ||
if (attributeUniqueId === uniqueId) { | ||
@@ -458,2 +539,4 @@ return attributeName; | ||
} | ||
// METADATA EXTRACTION | ||
/** Get top level metadata */ | ||
_getTopLevelMetadata(dracoGeometry) { | ||
@@ -463,2 +546,3 @@ const dracoMetadata = this.decoder.GetMetadata(dracoGeometry); | ||
} | ||
/** Get per attribute metadata */ | ||
_getAttributeMetadata(dracoGeometry, attributeId) { | ||
@@ -468,2 +552,7 @@ const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId); | ||
} | ||
/** | ||
* Extract metadata field values | ||
* @param dracoMetadata | ||
* @returns | ||
*/ | ||
_getDracoMetadata(dracoMetadata) { | ||
@@ -481,2 +570,7 @@ if (!dracoMetadata || !dracoMetadata.ptr) { | ||
} | ||
/** | ||
* Extracts possible values for one metadata entry by name | ||
* @param dracoMetadata | ||
* @param entryName | ||
*/ | ||
_getDracoMetadataField(dracoMetadata, entryName) { | ||
@@ -497,7 +591,6 @@ const dracoArray = new this.draco.DracoInt32Array(); | ||
} | ||
// QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION) | ||
/** Skip transforms for specific attribute types */ | ||
_disableAttributeTransforms(options) { | ||
const { | ||
quantizedAttributes = [], | ||
octahedronAttributes = [] | ||
} = options; | ||
const { quantizedAttributes = [], octahedronAttributes = [] } = options; | ||
const skipAttributes = [...quantizedAttributes, ...octahedronAttributes]; | ||
@@ -508,6 +601,8 @@ for (const dracoAttributeName of skipAttributes) { | ||
} | ||
/** | ||
* Extract (and apply?) Position Transform | ||
* @todo not used | ||
*/ | ||
_getQuantizationTransform(dracoAttribute, options) { | ||
const { | ||
quantizedAttributes = [] | ||
} = options; | ||
const { quantizedAttributes = [] } = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
@@ -532,5 +627,3 @@ const skip = quantizedAttributes.map((type) => this.decoder[type]).includes(attribute_type); | ||
_getOctahedronTransform(dracoAttribute, options) { | ||
const { | ||
octahedronAttributes = [] | ||
} = options; | ||
const { octahedronAttributes = [] } = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
@@ -552,2 +645,3 @@ const octahedron = octahedronAttributes.map((type) => this.decoder[type]).includes(attribute_type); | ||
} | ||
// HELPERS | ||
}; | ||
@@ -592,10 +686,13 @@ function getDracoDataType(draco, attributeType) { | ||
// ../worker-utils/src/lib/env-utils/version.ts | ||
var NPM_TAG = "latest"; | ||
function getVersion() { | ||
if (!globalThis._loadersgl_?.version) { | ||
globalThis._loadersgl_ = globalThis._loadersgl_ || {}; | ||
if (false) { | ||
console.warn("loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN."); | ||
if (typeof __VERSION__ === "undefined") { | ||
console.warn( | ||
"loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN." | ||
); | ||
globalThis._loadersgl_.version = NPM_TAG; | ||
} else { | ||
globalThis._loadersgl_.version = "4.2.0-alpha.4"; | ||
globalThis._loadersgl_.version = __VERSION__; | ||
} | ||
@@ -625,3 +722,6 @@ } | ||
var document_ = globals.document || {}; | ||
var isBrowser = typeof process !== "object" || String(process) !== "[object process]" || process.browser; | ||
var isBrowser = ( | ||
// @ts-ignore process.browser | ||
typeof process !== "object" || String(process) !== "[object process]" || process.browser | ||
); | ||
var isWorker = typeof importScripts === "function"; | ||
@@ -639,3 +739,4 @@ var isMobile = typeof window !== "undefined" && typeof window.orientation !== "undefined"; | ||
} | ||
loadLibraryPromises[libraryUrl] = loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl); | ||
loadLibraryPromises[libraryUrl] = // eslint-disable-next-line @typescript-eslint/no-misused-promises | ||
loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl); | ||
return await loadLibraryPromises[libraryUrl]; | ||
@@ -720,5 +821,9 @@ } | ||
var DRACO_EXTERNAL_LIBRARIES = { | ||
/** The primary Draco3D encoder, javascript wrapper part */ | ||
DECODER: "draco_wasm_wrapper.js", | ||
/** The primary draco decoder, compiled web assembly part */ | ||
DECODER_WASM: "draco_decoder.wasm", | ||
/** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */ | ||
FALLBACK_DECODER: "draco_decoder.js", | ||
/** Draco encoder */ | ||
ENCODER: "draco_encoder.js" | ||
@@ -738,5 +843,3 @@ }; | ||
loadDecoderPromise = loadDecoderPromise || modules.draco3d.createDecoderModule({}).then((draco) => { | ||
return { | ||
draco | ||
}; | ||
return { draco }; | ||
}); | ||
@@ -752,5 +855,3 @@ } else { | ||
loadEncoderPromise = loadEncoderPromise || modules.draco3d.createEncoderModule({}).then((draco) => { | ||
return { | ||
draco | ||
}; | ||
return { draco }; | ||
}); | ||
@@ -767,7 +868,25 @@ } else { | ||
case "js": | ||
DracoDecoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER); | ||
DracoDecoderModule = await loadLibrary( | ||
DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], | ||
"draco", | ||
options, | ||
DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER | ||
); | ||
break; | ||
case "wasm": | ||
default: | ||
[DracoDecoderModule, wasmBinary] = await Promise.all([await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER), await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM)]); | ||
[DracoDecoderModule, wasmBinary] = await Promise.all([ | ||
await loadLibrary( | ||
DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], | ||
"draco", | ||
options, | ||
DRACO_EXTERNAL_LIBRARIES.DECODER | ||
), | ||
await loadLibrary( | ||
DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], | ||
"draco", | ||
options, | ||
DRACO_EXTERNAL_LIBRARIES.DECODER_WASM | ||
) | ||
]); | ||
} | ||
@@ -785,5 +904,4 @@ DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule; | ||
...options, | ||
onModuleLoaded: (draco) => resolve({ | ||
draco | ||
}) | ||
onModuleLoaded: (draco) => resolve({ draco }) | ||
// Module is Promise-like. Wrap in object to avoid loop. | ||
}); | ||
@@ -793,9 +911,13 @@ }); | ||
async function loadDracoEncoder(options) { | ||
let DracoEncoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.ENCODER); | ||
let DracoEncoderModule = await loadLibrary( | ||
DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], | ||
"draco", | ||
options, | ||
DRACO_EXTERNAL_LIBRARIES.ENCODER | ||
); | ||
DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule; | ||
return new Promise((resolve) => { | ||
DracoEncoderModule({ | ||
onModuleLoaded: (draco) => resolve({ | ||
draco | ||
}) | ||
onModuleLoaded: (draco) => resolve({ draco }) | ||
// Module is Promise-like. Wrap in object to avoid loop. | ||
}); | ||
@@ -815,2 +937,3 @@ }); | ||
var DracoBuilder = class { | ||
// draco - the draco decoder, either import `draco3d` or load dynamically | ||
constructor(draco) { | ||
@@ -830,2 +953,3 @@ this.draco = draco; | ||
} | ||
// TBD - when does this need to be called? | ||
destroyEncodedObject(object) { | ||
@@ -836,2 +960,7 @@ if (object) { | ||
} | ||
/** | ||
* Encode mesh or point cloud | ||
* @param mesh =({}) | ||
* @param options | ||
*/ | ||
encodeSync(mesh, options = {}) { | ||
@@ -842,7 +971,5 @@ this.log = noop; | ||
} | ||
// PRIVATE | ||
_getAttributesFromMesh(mesh) { | ||
const attributes = { | ||
...mesh, | ||
...mesh.attributes | ||
}; | ||
const attributes = { ...mesh, ...mesh.attributes }; | ||
if (mesh.indices) { | ||
@@ -862,3 +989,7 @@ attributes.indices = mesh.indices; | ||
try { | ||
const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData); | ||
const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer( | ||
dracoPointCloud, | ||
false, | ||
dracoData | ||
); | ||
if (!(encodedLen > 0)) { | ||
@@ -896,2 +1027,6 @@ throw new Error("Draco encoding failed."); | ||
} | ||
/** | ||
* Set encoding options. | ||
* @param {{speed?: any; method?: any; quantization?: any;}} options | ||
*/ | ||
_setOptions(options) { | ||
@@ -913,2 +1048,7 @@ if ("speed" in options) { | ||
} | ||
/** | ||
* @param {Mesh} dracoMesh | ||
* @param {object} attributes | ||
* @returns {Mesh} | ||
*/ | ||
_createDracoMesh(dracoMesh, attributes, options) { | ||
@@ -939,2 +1079,6 @@ const optionalMetadata = options.attributesMetadata || {}; | ||
} | ||
/** | ||
* @param {} dracoPointCloud | ||
* @param {object} attributes | ||
*/ | ||
_createDracoPointCloud(dracoPointCloud, attributes, options) { | ||
@@ -951,3 +1095,8 @@ const optionalMetadata = options.attributesMetadata || {}; | ||
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; | ||
const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount); | ||
const uniqueId = this._addAttributeToMesh( | ||
dracoPointCloud, | ||
attributeName, | ||
attribute, | ||
vertexCount | ||
); | ||
if (uniqueId !== -1) { | ||
@@ -966,2 +1115,8 @@ this._addAttributeMetadata(dracoPointCloud, uniqueId, { | ||
} | ||
/** | ||
* @param mesh | ||
* @param attributeName | ||
* @param attribute | ||
* @param vertexCount | ||
*/ | ||
_addAttributeToMesh(mesh, attributeName, attribute, vertexCount) { | ||
@@ -981,5 +1136,3 @@ if (!ArrayBuffer.isView(attribute)) { | ||
const builder = this.dracoMeshBuilder; | ||
const { | ||
buffer | ||
} = attribute; | ||
const { buffer } = attribute; | ||
switch (attribute.constructor) { | ||
@@ -1004,2 +1157,7 @@ case Int8Array: | ||
} | ||
/** | ||
* DRACO can compress attributes of know type better | ||
* TODO - expose an attribute type map? | ||
* @param attributeName | ||
*/ | ||
_getDracoAttributeType(attributeName) { | ||
@@ -1036,2 +1194,7 @@ switch (attributeName.toLowerCase()) { | ||
} | ||
/** | ||
* Add metadata for the geometry. | ||
* @param dracoGeometry - WASM Draco Object | ||
* @param metadata | ||
*/ | ||
_addGeometryMetadata(dracoGeometry, metadata) { | ||
@@ -1042,7 +1205,22 @@ const dracoMetadata = new this.draco.Metadata(); | ||
} | ||
/** | ||
* Add metadata for an attribute to geometry. | ||
* @param dracoGeometry - WASM Draco Object | ||
* @param uniqueAttributeId | ||
* @param metadata | ||
*/ | ||
_addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) { | ||
const dracoAttributeMetadata = new this.draco.Metadata(); | ||
this._populateDracoMetadata(dracoAttributeMetadata, metadata); | ||
this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata); | ||
this.dracoMeshBuilder.SetMetadataForAttribute( | ||
dracoGeometry, | ||
uniqueAttributeId, | ||
dracoAttributeMetadata | ||
); | ||
} | ||
/** | ||
* Add contents of object or map to a WASM Draco Metadata Object | ||
* @param dracoMetadata - WASM Draco Object | ||
* @param metadata | ||
*/ | ||
_populateDracoMetadata(dracoMetadata, metadata) { | ||
@@ -1087,3 +1265,10 @@ for (const [key, value] of getEntries(metadata)) { | ||
pointcloud: false, | ||
// Set to true if pointcloud (mode: 0, no indices) | ||
attributeNameEntry: "name" | ||
// Draco Compression Parameters | ||
// method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults | ||
// speed: [5, 5], // Use draco defaults | ||
// quantization: { // Use draco defaults | ||
// POSITION: 10 | ||
// } | ||
}; | ||
@@ -1102,5 +1287,3 @@ var DracoWriter = { | ||
async function encode(data, options = {}) { | ||
const { | ||
draco | ||
} = await loadDracoEncoderModule(options); | ||
const { draco } = await loadDracoEncoderModule(options); | ||
const dracoBuilder = new DracoBuilder(draco); | ||
@@ -1131,5 +1314,3 @@ try { | ||
async function parse(arrayBuffer, options) { | ||
const { | ||
draco | ||
} = await loadDracoDecoderModule(options); | ||
const { draco } = await loadDracoDecoderModule(options); | ||
const dracoParser = new DracoParser(draco); | ||
@@ -1142,5 +1323,5 @@ try { | ||
} | ||
return __toCommonJS(src_exports); | ||
return __toCommonJS(bundle_exports); | ||
})(); | ||
return __exports__; | ||
}); |
import type { Loader, LoaderOptions } from '@loaders.gl/loader-utils'; | ||
import type { DracoMesh } from './lib/draco-types'; | ||
import type { DracoParseOptions } from './lib/draco-parser'; | ||
import type { DracoMesh } from "./lib/draco-types.js"; | ||
import type { DracoParseOptions } from "./lib/draco-parser.js"; | ||
export type DracoLoaderOptions = LoaderOptions & { | ||
@@ -5,0 +5,0 @@ draco?: DracoParseOptions & { |
@@ -0,21 +1,27 @@ | ||
// loaders.gl | ||
// SPDX-License-Identifier: MIT | ||
// Copyright (c) vis.gl contributors | ||
import { VERSION } from "./lib/utils/version.js"; | ||
/** | ||
* Worker loader for Draco3D compressed geometries | ||
*/ | ||
export const DracoLoader = { | ||
name: 'Draco', | ||
id: 'draco', | ||
module: 'draco', | ||
version: VERSION, | ||
worker: true, | ||
extensions: ['drc'], | ||
mimeTypes: ['application/octet-stream'], | ||
binary: true, | ||
tests: ['DRACO'], | ||
options: { | ||
draco: { | ||
decoderType: typeof WebAssembly === 'object' ? 'wasm' : 'js', | ||
libraryPath: 'libs/', | ||
extraAttributes: {}, | ||
attributeNameEntry: undefined | ||
name: 'Draco', | ||
id: 'draco', | ||
module: 'draco', | ||
// shapes: ['mesh'], | ||
version: VERSION, | ||
worker: true, | ||
extensions: ['drc'], | ||
mimeTypes: ['application/octet-stream'], | ||
binary: true, | ||
tests: ['DRACO'], | ||
options: { | ||
draco: { | ||
decoderType: typeof WebAssembly === 'object' ? 'wasm' : 'js', // 'js' for IE11 | ||
libraryPath: 'libs/', | ||
extraAttributes: {}, | ||
attributeNameEntry: undefined | ||
} | ||
} | ||
} | ||
}; | ||
//# sourceMappingURL=draco-loader.js.map |
import type { WriterWithEncoder, WriterOptions } from '@loaders.gl/loader-utils'; | ||
import type { DracoMesh } from './lib/draco-types'; | ||
import type { DracoBuildOptions } from './lib/draco-builder'; | ||
import type { DracoMesh } from "./lib/draco-types.js"; | ||
import type { DracoBuildOptions } from "./lib/draco-builder.js"; | ||
/** Writer Options for draco */ | ||
@@ -5,0 +5,0 @@ export type DracoWriterOptions = WriterOptions & { |
@@ -5,28 +5,35 @@ import DRACOBuilder from "./lib/draco-builder.js"; | ||
const DEFAULT_DRACO_WRITER_OPTIONS = { | ||
pointcloud: false, | ||
attributeNameEntry: 'name' | ||
pointcloud: false, // Set to true if pointcloud (mode: 0, no indices) | ||
attributeNameEntry: 'name' | ||
// Draco Compression Parameters | ||
// method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults | ||
// speed: [5, 5], // Use draco defaults | ||
// quantization: { // Use draco defaults | ||
// POSITION: 10 | ||
// } | ||
}; | ||
/** | ||
* Exporter for Draco3D compressed geometries | ||
*/ | ||
export const DracoWriter = { | ||
name: 'DRACO', | ||
id: 'draco', | ||
module: 'draco', | ||
version: VERSION, | ||
extensions: ['drc'], | ||
options: { | ||
draco: DEFAULT_DRACO_WRITER_OPTIONS | ||
}, | ||
encode | ||
name: 'DRACO', | ||
id: 'draco', | ||
module: 'draco', | ||
version: VERSION, | ||
extensions: ['drc'], | ||
options: { | ||
draco: DEFAULT_DRACO_WRITER_OPTIONS | ||
}, | ||
encode | ||
}; | ||
async function encode(data) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const { | ||
draco | ||
} = await loadDracoEncoderModule(options); | ||
const dracoBuilder = new DRACOBuilder(draco); | ||
try { | ||
return dracoBuilder.encodeSync(data, options.draco); | ||
} finally { | ||
dracoBuilder.destroy(); | ||
} | ||
async function encode(data, options = {}) { | ||
// Dynamically load draco | ||
const { draco } = await loadDracoEncoderModule(options); | ||
const dracoBuilder = new DRACOBuilder(draco); | ||
try { | ||
return dracoBuilder.encodeSync(data, options.draco); | ||
} | ||
finally { | ||
dracoBuilder.destroy(); | ||
} | ||
} | ||
//# sourceMappingURL=draco-writer.js.map |
@@ -1,41 +0,48 @@ | ||
export let draco_GeometryAttribute_Type = function (draco_GeometryAttribute_Type) { | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::INVALID"] = 0] = "draco_GeometryAttribute::INVALID"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::POSITION"] = 1] = "draco_GeometryAttribute::POSITION"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::NORMAL"] = 2] = "draco_GeometryAttribute::NORMAL"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::COLOR"] = 3] = "draco_GeometryAttribute::COLOR"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::TEX_COORD"] = 4] = "draco_GeometryAttribute::TEX_COORD"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::GENERIC"] = 5] = "draco_GeometryAttribute::GENERIC"; | ||
return draco_GeometryAttribute_Type; | ||
}({}); | ||
export let draco_EncodedGeometryType = function (draco_EncodedGeometryType) { | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::INVALID_GEOMETRY_TYPE"] = 0] = "draco::INVALID_GEOMETRY_TYPE"; | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::POINT_CLOUD"] = 1] = "draco::POINT_CLOUD"; | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::TRIANGULAR_MESH"] = 2] = "draco::TRIANGULAR_MESH"; | ||
return draco_EncodedGeometryType; | ||
}({}); | ||
export let draco_DataType = function (draco_DataType) { | ||
draco_DataType[draco_DataType["draco::DT_INVALID"] = 0] = "draco::DT_INVALID"; | ||
draco_DataType[draco_DataType["draco::DT_INT8"] = 1] = "draco::DT_INT8"; | ||
draco_DataType[draco_DataType["draco::DT_UINT8"] = 2] = "draco::DT_UINT8"; | ||
draco_DataType[draco_DataType["draco::DT_INT16"] = 3] = "draco::DT_INT16"; | ||
draco_DataType[draco_DataType["draco::DT_UINT16"] = 4] = "draco::DT_UINT16"; | ||
draco_DataType[draco_DataType["draco::DT_INT32"] = 5] = "draco::DT_INT32"; | ||
draco_DataType[draco_DataType["draco::DT_UINT32"] = 6] = "draco::DT_UINT32"; | ||
draco_DataType[draco_DataType["draco::DT_INT64"] = 7] = "draco::DT_INT64"; | ||
draco_DataType[draco_DataType["draco::DT_UINT64"] = 8] = "draco::DT_UINT64"; | ||
draco_DataType[draco_DataType["draco::DT_FLOAT32"] = 9] = "draco::DT_FLOAT32"; | ||
draco_DataType[draco_DataType["draco::DT_FLOAT64"] = 10] = "draco::DT_FLOAT64"; | ||
draco_DataType[draco_DataType["draco::DT_BOOL"] = 11] = "draco::DT_BOOL"; | ||
draco_DataType[draco_DataType["draco::DT_TYPES_COUNT"] = 12] = "draco::DT_TYPES_COUNT"; | ||
return draco_DataType; | ||
}({}); | ||
export let draco_StatusCode = function (draco_StatusCode) { | ||
draco_StatusCode[draco_StatusCode["draco_Status::OK"] = 0] = "draco_Status::OK"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::DRACO_ERROR"] = 1] = "draco_Status::DRACO_ERROR"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::IO_ERROR"] = 2] = "draco_Status::IO_ERROR"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::INVALID_PARAMETER"] = 3] = "draco_Status::INVALID_PARAMETER"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::UNSUPPORTED_VERSION"] = 4] = "draco_Status::UNSUPPORTED_VERSION"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::UNKNOWN_VERSION"] = 5] = "draco_Status::UNKNOWN_VERSION"; | ||
return draco_StatusCode; | ||
}({}); | ||
//# sourceMappingURL=draco3d-types.js.map | ||
// A set of typescript types manually adapted from the Draco web IDL | ||
// Draco JS is a bit tricky to work with due to the C++ emscripten code base | ||
// sparse documentation, so these types provide an extra safety net. | ||
// DRACO WEB DECODER IDL | ||
/** Draco3D geometry attribute type */ | ||
export var draco_GeometryAttribute_Type; | ||
(function (draco_GeometryAttribute_Type) { | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::INVALID"] = 0] = "draco_GeometryAttribute::INVALID"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::POSITION"] = 1] = "draco_GeometryAttribute::POSITION"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::NORMAL"] = 2] = "draco_GeometryAttribute::NORMAL"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::COLOR"] = 3] = "draco_GeometryAttribute::COLOR"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::TEX_COORD"] = 4] = "draco_GeometryAttribute::TEX_COORD"; | ||
draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::GENERIC"] = 5] = "draco_GeometryAttribute::GENERIC"; | ||
})(draco_GeometryAttribute_Type || (draco_GeometryAttribute_Type = {})); | ||
/** Draco3D encoded geometry type */ | ||
export var draco_EncodedGeometryType; | ||
(function (draco_EncodedGeometryType) { | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::INVALID_GEOMETRY_TYPE"] = 0] = "draco::INVALID_GEOMETRY_TYPE"; | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::POINT_CLOUD"] = 1] = "draco::POINT_CLOUD"; | ||
draco_EncodedGeometryType[draco_EncodedGeometryType["draco::TRIANGULAR_MESH"] = 2] = "draco::TRIANGULAR_MESH"; | ||
})(draco_EncodedGeometryType || (draco_EncodedGeometryType = {})); | ||
/** Draco3D data type */ | ||
export var draco_DataType; | ||
(function (draco_DataType) { | ||
draco_DataType[draco_DataType["draco::DT_INVALID"] = 0] = "draco::DT_INVALID"; | ||
draco_DataType[draco_DataType["draco::DT_INT8"] = 1] = "draco::DT_INT8"; | ||
draco_DataType[draco_DataType["draco::DT_UINT8"] = 2] = "draco::DT_UINT8"; | ||
draco_DataType[draco_DataType["draco::DT_INT16"] = 3] = "draco::DT_INT16"; | ||
draco_DataType[draco_DataType["draco::DT_UINT16"] = 4] = "draco::DT_UINT16"; | ||
draco_DataType[draco_DataType["draco::DT_INT32"] = 5] = "draco::DT_INT32"; | ||
draco_DataType[draco_DataType["draco::DT_UINT32"] = 6] = "draco::DT_UINT32"; | ||
draco_DataType[draco_DataType["draco::DT_INT64"] = 7] = "draco::DT_INT64"; | ||
draco_DataType[draco_DataType["draco::DT_UINT64"] = 8] = "draco::DT_UINT64"; | ||
draco_DataType[draco_DataType["draco::DT_FLOAT32"] = 9] = "draco::DT_FLOAT32"; | ||
draco_DataType[draco_DataType["draco::DT_FLOAT64"] = 10] = "draco::DT_FLOAT64"; | ||
draco_DataType[draco_DataType["draco::DT_BOOL"] = 11] = "draco::DT_BOOL"; | ||
draco_DataType[draco_DataType["draco::DT_TYPES_COUNT"] = 12] = "draco::DT_TYPES_COUNT"; | ||
})(draco_DataType || (draco_DataType = {})); | ||
/** Draco3D status code */ | ||
export var draco_StatusCode; | ||
(function (draco_StatusCode) { | ||
draco_StatusCode[draco_StatusCode["draco_Status::OK"] = 0] = "draco_Status::OK"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::DRACO_ERROR"] = 1] = "draco_Status::DRACO_ERROR"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::IO_ERROR"] = 2] = "draco_Status::IO_ERROR"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::INVALID_PARAMETER"] = 3] = "draco_Status::INVALID_PARAMETER"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::UNSUPPORTED_VERSION"] = 4] = "draco_Status::UNSUPPORTED_VERSION"; | ||
draco_StatusCode[draco_StatusCode["draco_Status::UNKNOWN_VERSION"] = 5] = "draco_Status::UNKNOWN_VERSION"; | ||
})(draco_StatusCode || (draco_StatusCode = {})); |
import type { LoaderWithParser } from '@loaders.gl/loader-utils'; | ||
import type { DracoMesh, DracoLoaderData } from './lib/draco-types'; | ||
import type { DracoLoaderOptions } from './draco-loader'; | ||
import { DracoLoader as DracoWorkerLoader } from './draco-loader'; | ||
export { DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS } from './lib/draco-module-loader'; | ||
import type { DracoMesh, DracoLoaderData } from "./lib/draco-types.js"; | ||
import type { DracoLoaderOptions } from "./draco-loader.js"; | ||
import { DracoLoader as DracoWorkerLoader } from "./draco-loader.js"; | ||
export { DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS } from "./lib/draco-module-loader.js"; | ||
export type { DracoMesh, DracoLoaderData }; | ||
export type { DracoWriterOptions } from './draco-writer'; | ||
export { DracoWriter } from './draco-writer'; | ||
export type { DracoWriterOptions } from "./draco-writer.js"; | ||
export { DracoWriter } from "./draco-writer.js"; | ||
/** | ||
@@ -10,0 +10,0 @@ * Browser worker doesn't work because of issue during "draco_encoder.js" loading. |
@@ -5,31 +5,37 @@ import { DracoLoader as DracoWorkerLoader } from "./draco-loader.js"; | ||
import { VERSION } from "./lib/utils/version.js"; | ||
// Module constants | ||
export { DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS } from "./lib/draco-module-loader.js"; | ||
export { DracoWriter } from "./draco-writer.js"; | ||
/** | ||
* Browser worker doesn't work because of issue during "draco_encoder.js" loading. | ||
* 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. | ||
*/ | ||
export const DracoWriterWorker = { | ||
id: 'draco-writer', | ||
name: 'Draco compressed geometry writer', | ||
module: 'draco', | ||
version: VERSION, | ||
worker: true, | ||
options: { | ||
draco: {}, | ||
source: null | ||
} | ||
id: 'draco-writer', | ||
name: 'Draco compressed geometry writer', | ||
module: 'draco', | ||
version: VERSION, | ||
worker: true, | ||
options: { | ||
draco: {}, | ||
source: null | ||
} | ||
}; | ||
export { DracoWorkerLoader }; | ||
/** | ||
* Loader for Draco3D compressed geometries | ||
*/ | ||
export const DracoLoader = { | ||
...DracoWorkerLoader, | ||
parse | ||
...DracoWorkerLoader, | ||
parse | ||
}; | ||
async function parse(arrayBuffer, options) { | ||
const { | ||
draco | ||
} = await loadDracoDecoderModule(options); | ||
const dracoParser = new DracoParser(draco); | ||
try { | ||
return dracoParser.parseSync(arrayBuffer, options === null || options === void 0 ? void 0 : options.draco); | ||
} finally { | ||
dracoParser.destroy(); | ||
} | ||
const { draco } = await loadDracoDecoderModule(options); | ||
const dracoParser = new DracoParser(draco); | ||
try { | ||
return dracoParser.parseSync(arrayBuffer, options?.draco); | ||
} | ||
finally { | ||
dracoParser.destroy(); | ||
} | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -1,4 +0,4 @@ | ||
import type { Draco3D, Encoder, Mesh, MeshBuilder, PointCloud, Metadata, MetadataBuilder, draco_GeometryAttribute_Type } from '../draco3d/draco3d-types'; | ||
import type { Draco3D, Encoder, Mesh, MeshBuilder, PointCloud, Metadata, MetadataBuilder, draco_GeometryAttribute_Type } from "../draco3d/draco3d-types.js"; | ||
import type { TypedArray } from '@loaders.gl/schema'; | ||
import type { DracoMesh } from './draco-types'; | ||
import type { DracoMesh } from "./draco-types.js"; | ||
export type DracoBuildOptions = { | ||
@@ -5,0 +5,0 @@ pointcloud?: boolean; |
@@ -0,269 +1,335 @@ | ||
// Native Draco attribute names to GLTF attribute names. | ||
const GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = { | ||
POSITION: 'POSITION', | ||
NORMAL: 'NORMAL', | ||
COLOR_0: 'COLOR', | ||
TEXCOORD_0: 'TEX_COORD' | ||
POSITION: 'POSITION', | ||
NORMAL: 'NORMAL', | ||
COLOR_0: 'COLOR', | ||
TEXCOORD_0: 'TEX_COORD' | ||
}; | ||
const noop = () => {}; | ||
const noop = () => { }; | ||
export default class DracoBuilder { | ||
constructor(draco) { | ||
this.draco = void 0; | ||
this.dracoEncoder = void 0; | ||
this.dracoMeshBuilder = void 0; | ||
this.dracoMetadataBuilder = void 0; | ||
this.log = void 0; | ||
this.draco = draco; | ||
this.dracoEncoder = new this.draco.Encoder(); | ||
this.dracoMeshBuilder = new this.draco.MeshBuilder(); | ||
this.dracoMetadataBuilder = new this.draco.MetadataBuilder(); | ||
} | ||
destroy() { | ||
this.destroyEncodedObject(this.dracoMeshBuilder); | ||
this.destroyEncodedObject(this.dracoEncoder); | ||
this.destroyEncodedObject(this.dracoMetadataBuilder); | ||
this.dracoMeshBuilder = null; | ||
this.dracoEncoder = null; | ||
this.draco = null; | ||
} | ||
destroyEncodedObject(object) { | ||
if (object) { | ||
this.draco.destroy(object); | ||
// draco - the draco decoder, either import `draco3d` or load dynamically | ||
constructor(draco) { | ||
this.draco = draco; | ||
this.dracoEncoder = new this.draco.Encoder(); | ||
this.dracoMeshBuilder = new this.draco.MeshBuilder(); | ||
this.dracoMetadataBuilder = new this.draco.MetadataBuilder(); | ||
} | ||
} | ||
encodeSync(mesh) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.log = noop; | ||
this._setOptions(options); | ||
return options.pointcloud ? this._encodePointCloud(mesh, options) : this._encodeMesh(mesh, options); | ||
} | ||
_getAttributesFromMesh(mesh) { | ||
const attributes = { | ||
...mesh, | ||
...mesh.attributes | ||
}; | ||
if (mesh.indices) { | ||
attributes.indices = mesh.indices; | ||
destroy() { | ||
this.destroyEncodedObject(this.dracoMeshBuilder); | ||
this.destroyEncodedObject(this.dracoEncoder); | ||
this.destroyEncodedObject(this.dracoMetadataBuilder); | ||
// @ts-ignore | ||
this.dracoMeshBuilder = null; | ||
// @ts-ignore | ||
this.dracoEncoder = null; | ||
// @ts-ignore | ||
this.draco = null; | ||
} | ||
return attributes; | ||
} | ||
_encodePointCloud(pointcloud, options) { | ||
const dracoPointCloud = new this.draco.PointCloud(); | ||
if (options.metadata) { | ||
this._addGeometryMetadata(dracoPointCloud, options.metadata); | ||
// TBD - when does this need to be called? | ||
destroyEncodedObject(object) { | ||
if (object) { | ||
this.draco.destroy(object); | ||
} | ||
} | ||
const attributes = this._getAttributesFromMesh(pointcloud); | ||
this._createDracoPointCloud(dracoPointCloud, attributes, options); | ||
const dracoData = new this.draco.DracoInt8Array(); | ||
try { | ||
const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData); | ||
if (!(encodedLen > 0)) { | ||
throw new Error('Draco encoding failed.'); | ||
} | ||
this.log(`DRACO encoded ${dracoPointCloud.num_points()} points | ||
/** | ||
* Encode mesh or point cloud | ||
* @param mesh =({}) | ||
* @param options | ||
*/ | ||
encodeSync(mesh, options = {}) { | ||
this.log = noop; // TODO | ||
this._setOptions(options); | ||
return options.pointcloud | ||
? this._encodePointCloud(mesh, options) | ||
: this._encodeMesh(mesh, options); | ||
} | ||
// PRIVATE | ||
_getAttributesFromMesh(mesh) { | ||
// TODO - Change the encodePointCloud interface instead? | ||
const attributes = { ...mesh, ...mesh.attributes }; | ||
// Fold indices into the attributes | ||
if (mesh.indices) { | ||
attributes.indices = mesh.indices; | ||
} | ||
return attributes; | ||
} | ||
_encodePointCloud(pointcloud, options) { | ||
const dracoPointCloud = new this.draco.PointCloud(); | ||
if (options.metadata) { | ||
this._addGeometryMetadata(dracoPointCloud, options.metadata); | ||
} | ||
const attributes = this._getAttributesFromMesh(pointcloud); | ||
// Build a `DracoPointCloud` from the input data | ||
this._createDracoPointCloud(dracoPointCloud, attributes, options); | ||
const dracoData = new this.draco.DracoInt8Array(); | ||
try { | ||
const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData); | ||
if (!(encodedLen > 0)) { | ||
throw new Error('Draco encoding failed.'); | ||
} | ||
this.log(`DRACO encoded ${dracoPointCloud.num_points()} points | ||
with ${dracoPointCloud.num_attributes()} attributes into ${encodedLen} bytes`); | ||
return dracoInt8ArrayToArrayBuffer(dracoData); | ||
} finally { | ||
this.destroyEncodedObject(dracoData); | ||
this.destroyEncodedObject(dracoPointCloud); | ||
return dracoInt8ArrayToArrayBuffer(dracoData); | ||
} | ||
finally { | ||
this.destroyEncodedObject(dracoData); | ||
this.destroyEncodedObject(dracoPointCloud); | ||
} | ||
} | ||
} | ||
_encodeMesh(mesh, options) { | ||
const dracoMesh = new this.draco.Mesh(); | ||
if (options.metadata) { | ||
this._addGeometryMetadata(dracoMesh, options.metadata); | ||
} | ||
const attributes = this._getAttributesFromMesh(mesh); | ||
this._createDracoMesh(dracoMesh, attributes, options); | ||
const dracoData = new this.draco.DracoInt8Array(); | ||
try { | ||
const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData); | ||
if (encodedLen <= 0) { | ||
throw new Error('Draco encoding failed.'); | ||
} | ||
this.log(`DRACO encoded ${dracoMesh.num_points()} points | ||
_encodeMesh(mesh, options) { | ||
const dracoMesh = new this.draco.Mesh(); | ||
if (options.metadata) { | ||
this._addGeometryMetadata(dracoMesh, options.metadata); | ||
} | ||
const attributes = this._getAttributesFromMesh(mesh); | ||
// Build a `DracoMesh` from the input data | ||
this._createDracoMesh(dracoMesh, attributes, options); | ||
const dracoData = new this.draco.DracoInt8Array(); | ||
try { | ||
const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData); | ||
if (encodedLen <= 0) { | ||
throw new Error('Draco encoding failed.'); | ||
} | ||
this.log(`DRACO encoded ${dracoMesh.num_points()} points | ||
with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`); | ||
return dracoInt8ArrayToArrayBuffer(dracoData); | ||
} finally { | ||
this.destroyEncodedObject(dracoData); | ||
this.destroyEncodedObject(dracoMesh); | ||
return dracoInt8ArrayToArrayBuffer(dracoData); | ||
} | ||
finally { | ||
this.destroyEncodedObject(dracoData); | ||
this.destroyEncodedObject(dracoMesh); | ||
} | ||
} | ||
} | ||
_setOptions(options) { | ||
if ('speed' in options) { | ||
this.dracoEncoder.SetSpeedOptions(...options.speed); | ||
/** | ||
* Set encoding options. | ||
* @param {{speed?: any; method?: any; quantization?: any;}} options | ||
*/ | ||
_setOptions(options) { | ||
if ('speed' in options) { | ||
// @ts-ignore | ||
this.dracoEncoder.SetSpeedOptions(...options.speed); | ||
} | ||
if ('method' in options) { | ||
const dracoMethod = this.draco[options.method || 'MESH_SEQUENTIAL_ENCODING']; | ||
// assert(dracoMethod) | ||
this.dracoEncoder.SetEncodingMethod(dracoMethod); | ||
} | ||
if ('quantization' in options) { | ||
for (const attribute in options.quantization) { | ||
const bits = options.quantization[attribute]; | ||
const dracoPosition = this.draco[attribute]; | ||
this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits); | ||
} | ||
} | ||
} | ||
if ('method' in options) { | ||
const dracoMethod = this.draco[options.method || 'MESH_SEQUENTIAL_ENCODING']; | ||
this.dracoEncoder.SetEncodingMethod(dracoMethod); | ||
/** | ||
* @param {Mesh} dracoMesh | ||
* @param {object} attributes | ||
* @returns {Mesh} | ||
*/ | ||
_createDracoMesh(dracoMesh, attributes, options) { | ||
const optionalMetadata = options.attributesMetadata || {}; | ||
try { | ||
const positions = this._getPositionAttribute(attributes); | ||
if (!positions) { | ||
throw new Error('positions'); | ||
} | ||
const vertexCount = positions.length / 3; | ||
for (let attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; | ||
const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount); | ||
if (uniqueId !== -1) { | ||
this._addAttributeMetadata(dracoMesh, uniqueId, { | ||
name: attributeName, | ||
...(optionalMetadata[attributeName] || {}) | ||
}); | ||
} | ||
} | ||
} | ||
catch (error) { | ||
this.destroyEncodedObject(dracoMesh); | ||
throw error; | ||
} | ||
return dracoMesh; | ||
} | ||
if ('quantization' in options) { | ||
for (const attribute in options.quantization) { | ||
const bits = options.quantization[attribute]; | ||
const dracoPosition = this.draco[attribute]; | ||
this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits); | ||
} | ||
/** | ||
* @param {} dracoPointCloud | ||
* @param {object} attributes | ||
*/ | ||
_createDracoPointCloud(dracoPointCloud, attributes, options) { | ||
const optionalMetadata = options.attributesMetadata || {}; | ||
try { | ||
const positions = this._getPositionAttribute(attributes); | ||
if (!positions) { | ||
throw new Error('positions'); | ||
} | ||
const vertexCount = positions.length / 3; | ||
for (let attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; | ||
const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount); | ||
if (uniqueId !== -1) { | ||
this._addAttributeMetadata(dracoPointCloud, uniqueId, { | ||
name: attributeName, | ||
...(optionalMetadata[attributeName] || {}) | ||
}); | ||
} | ||
} | ||
} | ||
catch (error) { | ||
this.destroyEncodedObject(dracoPointCloud); | ||
throw error; | ||
} | ||
return dracoPointCloud; | ||
} | ||
} | ||
_createDracoMesh(dracoMesh, attributes, options) { | ||
const optionalMetadata = options.attributesMetadata || {}; | ||
try { | ||
const positions = this._getPositionAttribute(attributes); | ||
if (!positions) { | ||
throw new Error('positions'); | ||
} | ||
const vertexCount = positions.length / 3; | ||
for (let attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; | ||
const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount); | ||
if (uniqueId !== -1) { | ||
this._addAttributeMetadata(dracoMesh, uniqueId, { | ||
name: attributeName, | ||
...(optionalMetadata[attributeName] || {}) | ||
}); | ||
/** | ||
* @param mesh | ||
* @param attributeName | ||
* @param attribute | ||
* @param vertexCount | ||
*/ | ||
_addAttributeToMesh(mesh, attributeName, attribute, vertexCount) { | ||
if (!ArrayBuffer.isView(attribute)) { | ||
return -1; | ||
} | ||
} | ||
} catch (error) { | ||
this.destroyEncodedObject(dracoMesh); | ||
throw error; | ||
const type = this._getDracoAttributeType(attributeName); | ||
// @ts-ignore TODO/fix types | ||
const size = attribute.length / vertexCount; | ||
if (type === 'indices') { | ||
// @ts-ignore TODO/fix types | ||
const numFaces = attribute.length / 3; | ||
this.log(`Adding attribute ${attributeName}, size ${numFaces}`); | ||
// @ts-ignore assumes mesh is a Mesh, not a point cloud | ||
this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute); | ||
return -1; | ||
} | ||
this.log(`Adding attribute ${attributeName}, size ${size}`); | ||
const builder = this.dracoMeshBuilder; | ||
const { buffer } = attribute; | ||
switch (attribute.constructor) { | ||
case Int8Array: | ||
return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer)); | ||
case Int16Array: | ||
return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer)); | ||
case Int32Array: | ||
return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer)); | ||
case Uint8Array: | ||
case Uint8ClampedArray: | ||
return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer)); | ||
case Uint16Array: | ||
return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer)); | ||
case Uint32Array: | ||
return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer)); | ||
case Float32Array: | ||
default: | ||
return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer)); | ||
} | ||
} | ||
return dracoMesh; | ||
} | ||
_createDracoPointCloud(dracoPointCloud, attributes, options) { | ||
const optionalMetadata = options.attributesMetadata || {}; | ||
try { | ||
const positions = this._getPositionAttribute(attributes); | ||
if (!positions) { | ||
throw new Error('positions'); | ||
} | ||
const vertexCount = positions.length / 3; | ||
for (let attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; | ||
const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount); | ||
if (uniqueId !== -1) { | ||
this._addAttributeMetadata(dracoPointCloud, uniqueId, { | ||
name: attributeName, | ||
...(optionalMetadata[attributeName] || {}) | ||
}); | ||
/** | ||
* DRACO can compress attributes of know type better | ||
* TODO - expose an attribute type map? | ||
* @param attributeName | ||
*/ | ||
_getDracoAttributeType(attributeName) { | ||
switch (attributeName.toLowerCase()) { | ||
case 'indices': | ||
return 'indices'; | ||
case 'position': | ||
case 'positions': | ||
case 'vertices': | ||
return this.draco.POSITION; | ||
case 'normal': | ||
case 'normals': | ||
return this.draco.NORMAL; | ||
case 'color': | ||
case 'colors': | ||
return this.draco.COLOR; | ||
case 'texcoord': | ||
case 'texcoords': | ||
return this.draco.TEX_COORD; | ||
default: | ||
return this.draco.GENERIC; | ||
} | ||
} | ||
} catch (error) { | ||
this.destroyEncodedObject(dracoPointCloud); | ||
throw error; | ||
} | ||
return dracoPointCloud; | ||
} | ||
_addAttributeToMesh(mesh, attributeName, attribute, vertexCount) { | ||
if (!ArrayBuffer.isView(attribute)) { | ||
return -1; | ||
_getPositionAttribute(attributes) { | ||
for (const attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
const dracoType = this._getDracoAttributeType(attributeName); | ||
if (dracoType === this.draco.POSITION) { | ||
return attribute; | ||
} | ||
} | ||
return null; | ||
} | ||
const type = this._getDracoAttributeType(attributeName); | ||
const size = attribute.length / vertexCount; | ||
if (type === 'indices') { | ||
const numFaces = attribute.length / 3; | ||
this.log(`Adding attribute ${attributeName}, size ${numFaces}`); | ||
this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute); | ||
return -1; | ||
/** | ||
* Add metadata for the geometry. | ||
* @param dracoGeometry - WASM Draco Object | ||
* @param metadata | ||
*/ | ||
_addGeometryMetadata(dracoGeometry, metadata) { | ||
const dracoMetadata = new this.draco.Metadata(); | ||
this._populateDracoMetadata(dracoMetadata, metadata); | ||
this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata); | ||
} | ||
this.log(`Adding attribute ${attributeName}, size ${size}`); | ||
const builder = this.dracoMeshBuilder; | ||
const { | ||
buffer | ||
} = attribute; | ||
switch (attribute.constructor) { | ||
case Int8Array: | ||
return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer)); | ||
case Int16Array: | ||
return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer)); | ||
case Int32Array: | ||
return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer)); | ||
case Uint8Array: | ||
case Uint8ClampedArray: | ||
return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer)); | ||
case Uint16Array: | ||
return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer)); | ||
case Uint32Array: | ||
return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer)); | ||
case Float32Array: | ||
default: | ||
return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer)); | ||
/** | ||
* Add metadata for an attribute to geometry. | ||
* @param dracoGeometry - WASM Draco Object | ||
* @param uniqueAttributeId | ||
* @param metadata | ||
*/ | ||
_addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) { | ||
// Note: Draco JS IDL doesn't seem to expose draco.AttributeMetadata, however it seems to | ||
// create such objects automatically from draco.Metadata object. | ||
const dracoAttributeMetadata = new this.draco.Metadata(); | ||
this._populateDracoMetadata(dracoAttributeMetadata, metadata); | ||
// Draco3d doc note: Directly add attribute metadata to geometry. | ||
// You can do this without explicitly adding |GeometryMetadata| to mesh. | ||
this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata); | ||
} | ||
} | ||
_getDracoAttributeType(attributeName) { | ||
switch (attributeName.toLowerCase()) { | ||
case 'indices': | ||
return 'indices'; | ||
case 'position': | ||
case 'positions': | ||
case 'vertices': | ||
return this.draco.POSITION; | ||
case 'normal': | ||
case 'normals': | ||
return this.draco.NORMAL; | ||
case 'color': | ||
case 'colors': | ||
return this.draco.COLOR; | ||
case 'texcoord': | ||
case 'texcoords': | ||
return this.draco.TEX_COORD; | ||
default: | ||
return this.draco.GENERIC; | ||
/** | ||
* Add contents of object or map to a WASM Draco Metadata Object | ||
* @param dracoMetadata - WASM Draco Object | ||
* @param metadata | ||
*/ | ||
_populateDracoMetadata(dracoMetadata, metadata) { | ||
for (const [key, value] of getEntries(metadata)) { | ||
switch (typeof value) { | ||
case 'number': | ||
if (Math.trunc(value) === value) { | ||
this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value); | ||
} | ||
else { | ||
this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value); | ||
} | ||
break; | ||
case 'object': | ||
if (value instanceof Int32Array) { | ||
this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length); | ||
} | ||
break; | ||
case 'string': | ||
default: | ||
this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value); | ||
} | ||
} | ||
} | ||
} | ||
_getPositionAttribute(attributes) { | ||
for (const attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
const dracoType = this._getDracoAttributeType(attributeName); | ||
if (dracoType === this.draco.POSITION) { | ||
return attribute; | ||
} | ||
} | ||
return null; | ||
} | ||
_addGeometryMetadata(dracoGeometry, metadata) { | ||
const dracoMetadata = new this.draco.Metadata(); | ||
this._populateDracoMetadata(dracoMetadata, metadata); | ||
this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata); | ||
} | ||
_addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) { | ||
const dracoAttributeMetadata = new this.draco.Metadata(); | ||
this._populateDracoMetadata(dracoAttributeMetadata, metadata); | ||
this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata); | ||
} | ||
_populateDracoMetadata(dracoMetadata, metadata) { | ||
for (const [key, value] of getEntries(metadata)) { | ||
switch (typeof value) { | ||
case 'number': | ||
if (Math.trunc(value) === value) { | ||
this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value); | ||
} else { | ||
this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value); | ||
} | ||
break; | ||
case 'object': | ||
if (value instanceof Int32Array) { | ||
this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length); | ||
} | ||
break; | ||
case 'string': | ||
default: | ||
this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value); | ||
} | ||
} | ||
} | ||
} | ||
// HELPER FUNCTIONS | ||
/** | ||
* Copy encoded data to buffer | ||
* @param dracoData | ||
*/ | ||
function dracoInt8ArrayToArrayBuffer(dracoData) { | ||
const byteLength = dracoData.size(); | ||
const outputBuffer = new ArrayBuffer(byteLength); | ||
const outputData = new Int8Array(outputBuffer); | ||
for (let i = 0; i < byteLength; ++i) { | ||
outputData[i] = dracoData.GetValue(i); | ||
} | ||
return outputBuffer; | ||
const byteLength = dracoData.size(); | ||
const outputBuffer = new ArrayBuffer(byteLength); | ||
const outputData = new Int8Array(outputBuffer); | ||
for (let i = 0; i < byteLength; ++i) { | ||
outputData[i] = dracoData.GetValue(i); | ||
} | ||
return outputBuffer; | ||
} | ||
/** Enable iteration over either an object or a map */ | ||
function getEntries(container) { | ||
const hasEntriesFunc = container.entries && !container.hasOwnProperty('entries'); | ||
return hasEntriesFunc ? container.entries() : Object.entries(container); | ||
const hasEntriesFunc = container.entries && !container.hasOwnProperty('entries'); | ||
return hasEntriesFunc ? container.entries() : Object.entries(container); | ||
} | ||
//# sourceMappingURL=draco-builder.js.map |
@@ -0,1 +1,4 @@ | ||
// Dynamic DRACO module loading inspired by THREE.DRACOLoader | ||
// https://github.com/mrdoob/three.js/blob/398c4f39ebdb8b23eefd4a7a5ec49ec0c96c7462/examples/jsm/loaders/DRACOLoader.js | ||
// by Don McCurdy / https://www.donmccurdy.com / MIT license | ||
import { loadLibrary } from '@loaders.gl/worker-utils'; | ||
@@ -6,12 +9,16 @@ const DRACO_DECODER_VERSION = '1.5.6'; | ||
export const DRACO_EXTERNAL_LIBRARIES = { | ||
DECODER: 'draco_wasm_wrapper.js', | ||
DECODER_WASM: 'draco_decoder.wasm', | ||
FALLBACK_DECODER: 'draco_decoder.js', | ||
ENCODER: 'draco_encoder.js' | ||
/** The primary Draco3D encoder, javascript wrapper part */ | ||
DECODER: 'draco_wasm_wrapper.js', | ||
/** The primary draco decoder, compiled web assembly part */ | ||
DECODER_WASM: 'draco_decoder.wasm', | ||
/** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */ | ||
FALLBACK_DECODER: 'draco_decoder.js', | ||
/** Draco encoder */ | ||
ENCODER: 'draco_encoder.js' | ||
}; | ||
export const DRACO_EXTERNAL_LIBRARY_URLS = { | ||
[DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`, | ||
[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`, | ||
[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`, | ||
[DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}` | ||
[DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`, | ||
[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`, | ||
[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`, | ||
[DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}` | ||
}; | ||
@@ -21,66 +28,75 @@ let loadDecoderPromise; | ||
export async function loadDracoDecoderModule(options) { | ||
const modules = options.modules || {}; | ||
if (modules.draco3d) { | ||
loadDecoderPromise = loadDecoderPromise || modules.draco3d.createDecoderModule({}).then(draco => { | ||
return { | ||
draco | ||
}; | ||
}); | ||
} else { | ||
loadDecoderPromise = loadDecoderPromise || loadDracoDecoder(options); | ||
} | ||
return await loadDecoderPromise; | ||
const modules = options.modules || {}; | ||
// Check if a bundled draco3d library has been supplied by application | ||
if (modules.draco3d) { | ||
loadDecoderPromise = | ||
loadDecoderPromise || | ||
modules.draco3d.createDecoderModule({}).then((draco) => { | ||
return { draco }; | ||
}); | ||
} | ||
else { | ||
// If not, dynamically load the WASM script from our CDN | ||
loadDecoderPromise = loadDecoderPromise || loadDracoDecoder(options); | ||
} | ||
return await loadDecoderPromise; | ||
} | ||
export async function loadDracoEncoderModule(options) { | ||
const modules = options.modules || {}; | ||
if (modules.draco3d) { | ||
loadEncoderPromise = loadEncoderPromise || modules.draco3d.createEncoderModule({}).then(draco => { | ||
return { | ||
draco | ||
}; | ||
}); | ||
} else { | ||
loadEncoderPromise = loadEncoderPromise || loadDracoEncoder(options); | ||
} | ||
return await loadEncoderPromise; | ||
const modules = options.modules || {}; | ||
// Check if a bundled draco3d library has been supplied by application | ||
if (modules.draco3d) { | ||
loadEncoderPromise = | ||
loadEncoderPromise || | ||
modules.draco3d.createEncoderModule({}).then((draco) => { | ||
return { draco }; | ||
}); | ||
} | ||
else { | ||
// If not, dynamically load the WASM script from our CDN | ||
loadEncoderPromise = loadEncoderPromise || loadDracoEncoder(options); | ||
} | ||
return await loadEncoderPromise; | ||
} | ||
// DRACO DECODER LOADING | ||
async function loadDracoDecoder(options) { | ||
let DracoDecoderModule; | ||
let wasmBinary; | ||
switch (options.draco && options.draco.decoderType) { | ||
case 'js': | ||
DracoDecoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER); | ||
break; | ||
case 'wasm': | ||
default: | ||
[DracoDecoderModule, wasmBinary] = await Promise.all([await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.DECODER), await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], 'draco', options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM)]); | ||
} | ||
DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule; | ||
return await initializeDracoDecoder(DracoDecoderModule, wasmBinary); | ||
let DracoDecoderModule; | ||
let wasmBinary; | ||
switch (options.draco && options.draco.decoderType) { | ||
case 'js': | ||
DracoDecoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER); | ||
break; | ||
case 'wasm': | ||
default: | ||
[DracoDecoderModule, wasmBinary] = await Promise.all([ | ||
await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.DECODER), | ||
await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], 'draco', options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM) | ||
]); | ||
} | ||
// Depends on how import happened... | ||
// @ts-ignore | ||
DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule; | ||
return await initializeDracoDecoder(DracoDecoderModule, wasmBinary); | ||
} | ||
function initializeDracoDecoder(DracoDecoderModule, wasmBinary) { | ||
const options = {}; | ||
if (wasmBinary) { | ||
options.wasmBinary = wasmBinary; | ||
} | ||
return new Promise(resolve => { | ||
DracoDecoderModule({ | ||
...options, | ||
onModuleLoaded: draco => resolve({ | ||
draco | ||
}) | ||
const options = {}; | ||
if (wasmBinary) { | ||
options.wasmBinary = wasmBinary; | ||
} | ||
return new Promise((resolve) => { | ||
DracoDecoderModule({ | ||
...options, | ||
onModuleLoaded: (draco) => resolve({ draco }) // Module is Promise-like. Wrap in object to avoid loop. | ||
}); | ||
}); | ||
}); | ||
} | ||
// ENCODER | ||
async function loadDracoEncoder(options) { | ||
let DracoEncoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.ENCODER); | ||
DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule; | ||
return new Promise(resolve => { | ||
DracoEncoderModule({ | ||
onModuleLoaded: draco => resolve({ | ||
draco | ||
}) | ||
let DracoEncoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], 'draco', options, DRACO_EXTERNAL_LIBRARIES.ENCODER); | ||
// @ts-ignore | ||
DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule; | ||
return new Promise((resolve) => { | ||
DracoEncoderModule({ | ||
onModuleLoaded: (draco) => resolve({ draco }) // Module is Promise-like. Wrap in object to avoid loop. | ||
}); | ||
}); | ||
}); | ||
} | ||
//# sourceMappingURL=draco-module-loader.js.map |
import type { TypedArray, MeshAttribute, MeshGeometry } from '@loaders.gl/schema'; | ||
import type { Draco3D, Decoder, Mesh, PointCloud, PointAttribute, Metadata, MetadataQuerier } from '../draco3d/draco3d-types'; | ||
import type { DracoMesh, DracoLoaderData, DracoAttribute, DracoMetadataEntry, DracoQuantizationTransform, DracoOctahedronTransform } from './draco-types'; | ||
import type { Draco3D, Decoder, Mesh, PointCloud, PointAttribute, Metadata, MetadataQuerier } from "../draco3d/draco3d-types.js"; | ||
import type { DracoMesh, DracoLoaderData, DracoAttribute, DracoMetadataEntry, DracoQuantizationTransform, DracoOctahedronTransform } from "./draco-types.js"; | ||
/** | ||
@@ -5,0 +5,0 @@ * @param topology - How triangle indices should be generated (mesh only) |
@@ -0,362 +1,473 @@ | ||
/* eslint-disable camelcase */ | ||
import { getMeshBoundingBox } from '@loaders.gl/schema'; | ||
import { getDracoSchema } from "./utils/get-draco-schema.js"; | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const GEOMETRY_TYPE = { | ||
TRIANGULAR_MESH: 0, | ||
POINT_CLOUD: 1 | ||
TRIANGULAR_MESH: 0, | ||
POINT_CLOUD: 1 | ||
}; | ||
// Native Draco attribute names to GLTF attribute names. | ||
const DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = { | ||
POSITION: 'POSITION', | ||
NORMAL: 'NORMAL', | ||
COLOR: 'COLOR_0', | ||
TEX_COORD: 'TEXCOORD_0' | ||
POSITION: 'POSITION', | ||
NORMAL: 'NORMAL', | ||
COLOR: 'COLOR_0', | ||
TEX_COORD: 'TEXCOORD_0' | ||
}; | ||
const DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = { | ||
1: Int8Array, | ||
2: Uint8Array, | ||
3: Int16Array, | ||
4: Uint16Array, | ||
5: Int32Array, | ||
6: Uint32Array, | ||
9: Float32Array | ||
1: Int8Array, | ||
2: Uint8Array, | ||
3: Int16Array, | ||
4: Uint16Array, | ||
5: Int32Array, | ||
6: Uint32Array, | ||
9: Float32Array | ||
}; | ||
const INDEX_ITEM_SIZE = 4; | ||
export default class DracoParser { | ||
constructor(draco) { | ||
this.draco = void 0; | ||
this.decoder = void 0; | ||
this.metadataQuerier = void 0; | ||
this.draco = draco; | ||
this.decoder = new this.draco.Decoder(); | ||
this.metadataQuerier = new this.draco.MetadataQuerier(); | ||
} | ||
destroy() { | ||
this.draco.destroy(this.decoder); | ||
this.draco.destroy(this.metadataQuerier); | ||
} | ||
parseSync(arrayBuffer) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
const buffer = new this.draco.DecoderBuffer(); | ||
buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength); | ||
this._disableAttributeTransforms(options); | ||
const geometry_type = this.decoder.GetEncodedGeometryType(buffer); | ||
const dracoGeometry = geometry_type === this.draco.TRIANGULAR_MESH ? new this.draco.Mesh() : new this.draco.PointCloud(); | ||
try { | ||
let dracoStatus; | ||
switch (geometry_type) { | ||
case this.draco.TRIANGULAR_MESH: | ||
dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry); | ||
break; | ||
case this.draco.POINT_CLOUD: | ||
dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); | ||
break; | ||
default: | ||
throw new Error('DRACO: Unknown geometry type.'); | ||
} | ||
if (!dracoStatus.ok() || !dracoGeometry.ptr) { | ||
const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`; | ||
throw new Error(message); | ||
} | ||
const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options); | ||
const geometry = this._getMeshData(dracoGeometry, loaderData, options); | ||
const boundingBox = getMeshBoundingBox(geometry.attributes); | ||
const schema = getDracoSchema(geometry.attributes, loaderData, geometry.indices); | ||
const data = { | ||
loader: 'draco', | ||
loaderData, | ||
header: { | ||
vertexCount: dracoGeometry.num_points(), | ||
boundingBox | ||
}, | ||
...geometry, | ||
schema | ||
}; | ||
return data; | ||
} finally { | ||
this.draco.destroy(buffer); | ||
if (dracoGeometry) { | ||
this.draco.destroy(dracoGeometry); | ||
} | ||
// draco - the draco decoder, either import `draco3d` or load dynamically | ||
constructor(draco) { | ||
this.draco = draco; | ||
this.decoder = new this.draco.Decoder(); | ||
this.metadataQuerier = new this.draco.MetadataQuerier(); | ||
} | ||
} | ||
_getDracoLoaderData(dracoGeometry, geometry_type, options) { | ||
const metadata = this._getTopLevelMetadata(dracoGeometry); | ||
const attributes = this._getDracoAttributes(dracoGeometry, options); | ||
return { | ||
geometry_type, | ||
num_attributes: dracoGeometry.num_attributes(), | ||
num_points: dracoGeometry.num_points(), | ||
num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0, | ||
metadata, | ||
attributes | ||
}; | ||
} | ||
_getDracoAttributes(dracoGeometry, options) { | ||
const dracoAttributes = {}; | ||
for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); | ||
const metadata = this._getAttributeMetadata(dracoGeometry, attributeId); | ||
dracoAttributes[dracoAttribute.unique_id()] = { | ||
unique_id: dracoAttribute.unique_id(), | ||
attribute_type: dracoAttribute.attribute_type(), | ||
data_type: dracoAttribute.data_type(), | ||
num_components: dracoAttribute.num_components(), | ||
byte_offset: dracoAttribute.byte_offset(), | ||
byte_stride: dracoAttribute.byte_stride(), | ||
normalized: dracoAttribute.normalized(), | ||
attribute_index: attributeId, | ||
metadata | ||
}; | ||
const quantization = this._getQuantizationTransform(dracoAttribute, options); | ||
if (quantization) { | ||
dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization; | ||
} | ||
const octahedron = this._getOctahedronTransform(dracoAttribute, options); | ||
if (octahedron) { | ||
dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron; | ||
} | ||
/** | ||
* Destroy draco resources | ||
*/ | ||
destroy() { | ||
this.draco.destroy(this.decoder); | ||
this.draco.destroy(this.metadataQuerier); | ||
} | ||
return dracoAttributes; | ||
} | ||
_getMeshData(dracoGeometry, loaderData, options) { | ||
const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options); | ||
const positionAttribute = attributes.POSITION; | ||
if (!positionAttribute) { | ||
throw new Error('DRACO: No position attribute found.'); | ||
/** | ||
* NOTE: caller must call `destroyGeometry` on the return value after using it | ||
* @param arrayBuffer | ||
* @param options | ||
*/ | ||
parseSync(arrayBuffer, options = {}) { | ||
const buffer = new this.draco.DecoderBuffer(); | ||
buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength); | ||
this._disableAttributeTransforms(options); | ||
const geometry_type = this.decoder.GetEncodedGeometryType(buffer); | ||
const dracoGeometry = geometry_type === this.draco.TRIANGULAR_MESH | ||
? new this.draco.Mesh() | ||
: new this.draco.PointCloud(); | ||
try { | ||
let dracoStatus; | ||
switch (geometry_type) { | ||
case this.draco.TRIANGULAR_MESH: | ||
dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry); | ||
break; | ||
case this.draco.POINT_CLOUD: | ||
dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); | ||
break; | ||
default: | ||
throw new Error('DRACO: Unknown geometry type.'); | ||
} | ||
if (!dracoStatus.ok() || !dracoGeometry.ptr) { | ||
const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`; | ||
// console.error(message); | ||
throw new Error(message); | ||
} | ||
const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options); | ||
const geometry = this._getMeshData(dracoGeometry, loaderData, options); | ||
const boundingBox = getMeshBoundingBox(geometry.attributes); | ||
const schema = getDracoSchema(geometry.attributes, loaderData, geometry.indices); | ||
const data = { | ||
loader: 'draco', | ||
loaderData, | ||
header: { | ||
vertexCount: dracoGeometry.num_points(), | ||
boundingBox | ||
}, | ||
...geometry, | ||
schema | ||
}; | ||
return data; | ||
} | ||
finally { | ||
this.draco.destroy(buffer); | ||
if (dracoGeometry) { | ||
this.draco.destroy(dracoGeometry); | ||
} | ||
} | ||
} | ||
if (dracoGeometry instanceof this.draco.Mesh) { | ||
switch (options.topology) { | ||
case 'triangle-strip': | ||
return { | ||
topology: 'triangle-strip', | ||
mode: 4, | ||
attributes, | ||
indices: { | ||
value: this._getTriangleStripIndices(dracoGeometry), | ||
size: 1 | ||
// Draco specific "loader data" | ||
/** | ||
* Extract | ||
* @param dracoGeometry | ||
* @param geometry_type | ||
* @param options | ||
* @returns | ||
*/ | ||
_getDracoLoaderData(dracoGeometry, geometry_type, options) { | ||
const metadata = this._getTopLevelMetadata(dracoGeometry); | ||
const attributes = this._getDracoAttributes(dracoGeometry, options); | ||
return { | ||
geometry_type, | ||
num_attributes: dracoGeometry.num_attributes(), | ||
num_points: dracoGeometry.num_points(), | ||
num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0, | ||
metadata, | ||
attributes | ||
}; | ||
} | ||
/** | ||
* Extract all draco provided information and metadata for each attribute | ||
* @param dracoGeometry | ||
* @param options | ||
* @returns | ||
*/ | ||
_getDracoAttributes(dracoGeometry, options) { | ||
const dracoAttributes = {}; | ||
for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { | ||
// Note: Draco docs do not seem clear on `GetAttribute` ids just being a zero-based index, | ||
// but it does seems to work this way | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); | ||
const metadata = this._getAttributeMetadata(dracoGeometry, attributeId); | ||
dracoAttributes[dracoAttribute.unique_id()] = { | ||
unique_id: dracoAttribute.unique_id(), | ||
attribute_type: dracoAttribute.attribute_type(), | ||
data_type: dracoAttribute.data_type(), | ||
num_components: dracoAttribute.num_components(), | ||
byte_offset: dracoAttribute.byte_offset(), | ||
byte_stride: dracoAttribute.byte_stride(), | ||
normalized: dracoAttribute.normalized(), | ||
attribute_index: attributeId, | ||
metadata | ||
}; | ||
// Add transformation parameters for any attributes app wants untransformed | ||
const quantization = this._getQuantizationTransform(dracoAttribute, options); | ||
if (quantization) { | ||
dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization; | ||
} | ||
}; | ||
case 'triangle-list': | ||
default: | ||
return { | ||
topology: 'triangle-list', | ||
mode: 5, | ||
attributes, | ||
indices: { | ||
value: this._getTriangleListIndices(dracoGeometry), | ||
size: 1 | ||
const octahedron = this._getOctahedronTransform(dracoAttribute, options); | ||
if (octahedron) { | ||
dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron; | ||
} | ||
}; | ||
} | ||
} | ||
return dracoAttributes; | ||
} | ||
return { | ||
topology: 'point-list', | ||
mode: 0, | ||
attributes | ||
}; | ||
} | ||
_getMeshAttributes(loaderData, dracoGeometry, options) { | ||
const attributes = {}; | ||
for (const loaderAttribute of Object.values(loaderData.attributes)) { | ||
const attributeName = this._deduceAttributeName(loaderAttribute, options); | ||
loaderAttribute.name = attributeName; | ||
const { | ||
value, | ||
size | ||
} = this._getAttributeValues(dracoGeometry, loaderAttribute); | ||
attributes[attributeName] = { | ||
value, | ||
size, | ||
byteOffset: loaderAttribute.byte_offset, | ||
byteStride: loaderAttribute.byte_stride, | ||
normalized: loaderAttribute.normalized | ||
}; | ||
/** | ||
* Get standard loaders.gl mesh category data | ||
* Extracts the geometry from draco | ||
* @param dracoGeometry | ||
* @param options | ||
*/ | ||
_getMeshData(dracoGeometry, loaderData, options) { | ||
const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options); | ||
const positionAttribute = attributes.POSITION; | ||
if (!positionAttribute) { | ||
throw new Error('DRACO: No position attribute found.'); | ||
} | ||
// For meshes, we need indices to define the faces. | ||
if (dracoGeometry instanceof this.draco.Mesh) { | ||
switch (options.topology) { | ||
case 'triangle-strip': | ||
return { | ||
topology: 'triangle-strip', | ||
mode: 4, // GL.TRIANGLES | ||
attributes, | ||
indices: { | ||
value: this._getTriangleStripIndices(dracoGeometry), | ||
size: 1 | ||
} | ||
}; | ||
case 'triangle-list': | ||
default: | ||
return { | ||
topology: 'triangle-list', | ||
mode: 5, // GL.TRIANGLE_STRIP | ||
attributes, | ||
indices: { | ||
value: this._getTriangleListIndices(dracoGeometry), | ||
size: 1 | ||
} | ||
}; | ||
} | ||
} | ||
// PointCloud - must come last as Mesh inherits from PointCloud | ||
return { | ||
topology: 'point-list', | ||
mode: 0, // GL.POINTS | ||
attributes | ||
}; | ||
} | ||
return attributes; | ||
} | ||
_getTriangleListIndices(dracoGeometry) { | ||
const numFaces = dracoGeometry.num_faces(); | ||
const numIndices = numFaces * 3; | ||
const byteLength = numIndices * INDEX_ITEM_SIZE; | ||
const ptr = this.draco._malloc(byteLength); | ||
try { | ||
this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr); | ||
return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice(); | ||
} finally { | ||
this.draco._free(ptr); | ||
_getMeshAttributes(loaderData, dracoGeometry, options) { | ||
const attributes = {}; | ||
for (const loaderAttribute of Object.values(loaderData.attributes)) { | ||
const attributeName = this._deduceAttributeName(loaderAttribute, options); | ||
loaderAttribute.name = attributeName; | ||
const { value, size } = this._getAttributeValues(dracoGeometry, loaderAttribute); | ||
attributes[attributeName] = { | ||
value, | ||
size, | ||
byteOffset: loaderAttribute.byte_offset, | ||
byteStride: loaderAttribute.byte_stride, | ||
normalized: loaderAttribute.normalized | ||
}; | ||
} | ||
return attributes; | ||
} | ||
} | ||
_getTriangleStripIndices(dracoGeometry) { | ||
const dracoArray = new this.draco.DracoInt32Array(); | ||
try { | ||
this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray); | ||
return getUint32Array(dracoArray); | ||
} finally { | ||
this.draco.destroy(dracoArray); | ||
// MESH INDICES EXTRACTION | ||
/** | ||
* For meshes, we need indices to define the faces. | ||
* @param dracoGeometry | ||
*/ | ||
_getTriangleListIndices(dracoGeometry) { | ||
// Example on how to retrieve mesh and attributes. | ||
const numFaces = dracoGeometry.num_faces(); | ||
const numIndices = numFaces * 3; | ||
const byteLength = numIndices * INDEX_ITEM_SIZE; | ||
const ptr = this.draco._malloc(byteLength); | ||
try { | ||
this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr); | ||
return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice(); | ||
} | ||
finally { | ||
this.draco._free(ptr); | ||
} | ||
} | ||
} | ||
_getAttributeValues(dracoGeometry, attribute) { | ||
const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type]; | ||
const numComponents = attribute.num_components; | ||
const numPoints = dracoGeometry.num_points(); | ||
const numValues = numPoints * numComponents; | ||
const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT; | ||
const dataType = getDracoDataType(this.draco, TypedArrayCtor); | ||
let value; | ||
const ptr = this.draco._malloc(byteLength); | ||
try { | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index); | ||
this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr); | ||
value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice(); | ||
} finally { | ||
this.draco._free(ptr); | ||
/** | ||
* For meshes, we need indices to define the faces. | ||
* @param dracoGeometry | ||
*/ | ||
_getTriangleStripIndices(dracoGeometry) { | ||
const dracoArray = new this.draco.DracoInt32Array(); | ||
try { | ||
/* const numStrips = */ this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray); | ||
return getUint32Array(dracoArray); | ||
} | ||
finally { | ||
this.draco.destroy(dracoArray); | ||
} | ||
} | ||
return { | ||
value, | ||
size: numComponents | ||
}; | ||
} | ||
_deduceAttributeName(attribute, options) { | ||
const uniqueId = attribute.unique_id; | ||
for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) { | ||
if (attributeUniqueId === uniqueId) { | ||
return attributeName; | ||
} | ||
/** | ||
* | ||
* @param dracoGeometry | ||
* @param dracoAttribute | ||
* @param attributeName | ||
*/ | ||
_getAttributeValues(dracoGeometry, attribute) { | ||
const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type]; | ||
const numComponents = attribute.num_components; | ||
const numPoints = dracoGeometry.num_points(); | ||
const numValues = numPoints * numComponents; | ||
const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT; | ||
const dataType = getDracoDataType(this.draco, TypedArrayCtor); | ||
let value; | ||
const ptr = this.draco._malloc(byteLength); | ||
try { | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index); | ||
this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr); | ||
value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice(); | ||
} | ||
finally { | ||
this.draco._free(ptr); | ||
} | ||
return { value, size: numComponents }; | ||
} | ||
const thisAttributeType = attribute.attribute_type; | ||
for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) { | ||
const attributeType = this.draco[dracoAttributeConstant]; | ||
if (attributeType === thisAttributeType) { | ||
return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant]; | ||
// Attribute names | ||
/** | ||
* DRACO does not store attribute names - We need to deduce an attribute name | ||
* for each attribute | ||
_getAttributeNames( | ||
dracoGeometry: Mesh | PointCloud, | ||
options: DracoParseOptions | ||
): {[unique_id: number]: string} { | ||
const attributeNames: {[unique_id: number]: string} = {}; | ||
for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { | ||
const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); | ||
const attributeName = this._deduceAttributeName(dracoAttribute, options); | ||
attributeNames[attributeName] = attributeName; | ||
} | ||
return attributeNames; | ||
} | ||
const entryName = options.attributeNameEntry || 'name'; | ||
if (attribute.metadata[entryName]) { | ||
return attribute.metadata[entryName].string; | ||
*/ | ||
/** | ||
* Deduce an attribute name. | ||
* @note DRACO does not save attribute names, just general type (POSITION, COLOR) | ||
* to help optimize compression. We generate GLTF compatible names for the Draco-recognized | ||
* types | ||
* @param attributeData | ||
*/ | ||
_deduceAttributeName(attribute, options) { | ||
// Deduce name based on application provided map | ||
const uniqueId = attribute.unique_id; | ||
for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) { | ||
if (attributeUniqueId === uniqueId) { | ||
return attributeName; | ||
} | ||
} | ||
// Deduce name based on attribute type | ||
const thisAttributeType = attribute.attribute_type; | ||
for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) { | ||
const attributeType = this.draco[dracoAttributeConstant]; | ||
if (attributeType === thisAttributeType) { | ||
// TODO - Return unique names if there multiple attributes per type | ||
// (e.g. multiple TEX_COORDS or COLORS) | ||
return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant]; | ||
} | ||
} | ||
// Look up in metadata | ||
// TODO - shouldn't this have priority? | ||
const entryName = options.attributeNameEntry || 'name'; | ||
if (attribute.metadata[entryName]) { | ||
return attribute.metadata[entryName].string; | ||
} | ||
// Attribute of "GENERIC" type, we need to assign some name | ||
return `CUSTOM_ATTRIBUTE_${uniqueId}`; | ||
} | ||
return `CUSTOM_ATTRIBUTE_${uniqueId}`; | ||
} | ||
_getTopLevelMetadata(dracoGeometry) { | ||
const dracoMetadata = this.decoder.GetMetadata(dracoGeometry); | ||
return this._getDracoMetadata(dracoMetadata); | ||
} | ||
_getAttributeMetadata(dracoGeometry, attributeId) { | ||
const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId); | ||
return this._getDracoMetadata(dracoMetadata); | ||
} | ||
_getDracoMetadata(dracoMetadata) { | ||
if (!dracoMetadata || !dracoMetadata.ptr) { | ||
return {}; | ||
// METADATA EXTRACTION | ||
/** Get top level metadata */ | ||
_getTopLevelMetadata(dracoGeometry) { | ||
const dracoMetadata = this.decoder.GetMetadata(dracoGeometry); | ||
return this._getDracoMetadata(dracoMetadata); | ||
} | ||
const result = {}; | ||
const numEntries = this.metadataQuerier.NumEntries(dracoMetadata); | ||
for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) { | ||
const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex); | ||
result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName); | ||
/** Get per attribute metadata */ | ||
_getAttributeMetadata(dracoGeometry, attributeId) { | ||
const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId); | ||
return this._getDracoMetadata(dracoMetadata); | ||
} | ||
return result; | ||
} | ||
_getDracoMetadataField(dracoMetadata, entryName) { | ||
const dracoArray = new this.draco.DracoInt32Array(); | ||
try { | ||
this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray); | ||
const intArray = getInt32Array(dracoArray); | ||
return { | ||
int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName), | ||
string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName), | ||
double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName), | ||
intArray | ||
}; | ||
} finally { | ||
this.draco.destroy(dracoArray); | ||
/** | ||
* Extract metadata field values | ||
* @param dracoMetadata | ||
* @returns | ||
*/ | ||
_getDracoMetadata(dracoMetadata) { | ||
// The not so wonderful world of undocumented Draco APIs :( | ||
if (!dracoMetadata || !dracoMetadata.ptr) { | ||
return {}; | ||
} | ||
const result = {}; | ||
const numEntries = this.metadataQuerier.NumEntries(dracoMetadata); | ||
for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) { | ||
const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex); | ||
result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName); | ||
} | ||
return result; | ||
} | ||
} | ||
_disableAttributeTransforms(options) { | ||
const { | ||
quantizedAttributes = [], | ||
octahedronAttributes = [] | ||
} = options; | ||
const skipAttributes = [...quantizedAttributes, ...octahedronAttributes]; | ||
for (const dracoAttributeName of skipAttributes) { | ||
this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]); | ||
/** | ||
* Extracts possible values for one metadata entry by name | ||
* @param dracoMetadata | ||
* @param entryName | ||
*/ | ||
_getDracoMetadataField(dracoMetadata, entryName) { | ||
const dracoArray = new this.draco.DracoInt32Array(); | ||
try { | ||
// Draco metadata fields can hold int32 arrays | ||
this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray); | ||
const intArray = getInt32Array(dracoArray); | ||
return { | ||
int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName), | ||
string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName), | ||
double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName), | ||
intArray | ||
}; | ||
} | ||
finally { | ||
this.draco.destroy(dracoArray); | ||
} | ||
} | ||
} | ||
_getQuantizationTransform(dracoAttribute, options) { | ||
const { | ||
quantizedAttributes = [] | ||
} = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
const skip = quantizedAttributes.map(type => this.decoder[type]).includes(attribute_type); | ||
if (skip) { | ||
const transform = new this.draco.AttributeQuantizationTransform(); | ||
try { | ||
if (transform.InitFromAttribute(dracoAttribute)) { | ||
return { | ||
quantization_bits: transform.quantization_bits(), | ||
range: transform.range(), | ||
min_values: new Float32Array([1, 2, 3]).map(i => transform.min_value(i)) | ||
}; | ||
// QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION) | ||
/** Skip transforms for specific attribute types */ | ||
_disableAttributeTransforms(options) { | ||
const { quantizedAttributes = [], octahedronAttributes = [] } = options; | ||
const skipAttributes = [...quantizedAttributes, ...octahedronAttributes]; | ||
for (const dracoAttributeName of skipAttributes) { | ||
this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]); | ||
} | ||
} finally { | ||
this.draco.destroy(transform); | ||
} | ||
} | ||
return null; | ||
} | ||
_getOctahedronTransform(dracoAttribute, options) { | ||
const { | ||
octahedronAttributes = [] | ||
} = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
const octahedron = octahedronAttributes.map(type => this.decoder[type]).includes(attribute_type); | ||
if (octahedron) { | ||
const transform = new this.draco.AttributeQuantizationTransform(); | ||
try { | ||
if (transform.InitFromAttribute(dracoAttribute)) { | ||
return { | ||
quantization_bits: transform.quantization_bits() | ||
}; | ||
/** | ||
* Extract (and apply?) Position Transform | ||
* @todo not used | ||
*/ | ||
_getQuantizationTransform(dracoAttribute, options) { | ||
const { quantizedAttributes = [] } = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
const skip = quantizedAttributes.map((type) => this.decoder[type]).includes(attribute_type); | ||
if (skip) { | ||
const transform = new this.draco.AttributeQuantizationTransform(); | ||
try { | ||
if (transform.InitFromAttribute(dracoAttribute)) { | ||
return { | ||
quantization_bits: transform.quantization_bits(), | ||
range: transform.range(), | ||
min_values: new Float32Array([1, 2, 3]).map((i) => transform.min_value(i)) | ||
}; | ||
} | ||
} | ||
finally { | ||
this.draco.destroy(transform); | ||
} | ||
} | ||
} finally { | ||
this.draco.destroy(transform); | ||
} | ||
return null; | ||
} | ||
return null; | ||
} | ||
_getOctahedronTransform(dracoAttribute, options) { | ||
const { octahedronAttributes = [] } = options; | ||
const attribute_type = dracoAttribute.attribute_type(); | ||
const octahedron = octahedronAttributes | ||
.map((type) => this.decoder[type]) | ||
.includes(attribute_type); | ||
if (octahedron) { | ||
const transform = new this.draco.AttributeQuantizationTransform(); | ||
try { | ||
if (transform.InitFromAttribute(dracoAttribute)) { | ||
return { | ||
quantization_bits: transform.quantization_bits() | ||
}; | ||
} | ||
} | ||
finally { | ||
this.draco.destroy(transform); | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
/** | ||
* Get draco specific data type by TypedArray constructor type | ||
* @param attributeType | ||
* @returns draco specific data type | ||
*/ | ||
function getDracoDataType(draco, attributeType) { | ||
switch (attributeType) { | ||
case Float32Array: | ||
return draco.DT_FLOAT32; | ||
case Int8Array: | ||
return draco.DT_INT8; | ||
case Int16Array: | ||
return draco.DT_INT16; | ||
case Int32Array: | ||
return draco.DT_INT32; | ||
case Uint8Array: | ||
return draco.DT_UINT8; | ||
case Uint16Array: | ||
return draco.DT_UINT16; | ||
case Uint32Array: | ||
return draco.DT_UINT32; | ||
default: | ||
return draco.DT_INVALID; | ||
} | ||
switch (attributeType) { | ||
case Float32Array: | ||
return draco.DT_FLOAT32; | ||
case Int8Array: | ||
return draco.DT_INT8; | ||
case Int16Array: | ||
return draco.DT_INT16; | ||
case Int32Array: | ||
return draco.DT_INT32; | ||
case Uint8Array: | ||
return draco.DT_UINT8; | ||
case Uint16Array: | ||
return draco.DT_UINT16; | ||
case Uint32Array: | ||
return draco.DT_UINT32; | ||
default: | ||
return draco.DT_INVALID; | ||
} | ||
} | ||
/** | ||
* Copy a Draco int32 array into a JS typed array | ||
*/ | ||
function getInt32Array(dracoArray) { | ||
const numValues = dracoArray.size(); | ||
const intArray = new Int32Array(numValues); | ||
for (let i = 0; i < numValues; i++) { | ||
intArray[i] = dracoArray.GetValue(i); | ||
} | ||
return intArray; | ||
const numValues = dracoArray.size(); | ||
const intArray = new Int32Array(numValues); | ||
for (let i = 0; i < numValues; i++) { | ||
intArray[i] = dracoArray.GetValue(i); | ||
} | ||
return intArray; | ||
} | ||
/** | ||
* Copy a Draco int32 array into a JS typed array | ||
*/ | ||
function getUint32Array(dracoArray) { | ||
const numValues = dracoArray.size(); | ||
const intArray = new Int32Array(numValues); | ||
for (let i = 0; i < numValues; i++) { | ||
intArray[i] = dracoArray.GetValue(i); | ||
} | ||
return intArray; | ||
const numValues = dracoArray.size(); | ||
const intArray = new Int32Array(numValues); | ||
for (let i = 0; i < numValues; i++) { | ||
intArray[i] = dracoArray.GetValue(i); | ||
} | ||
return intArray; | ||
} | ||
//# sourceMappingURL=draco-parser.js.map |
@@ -0,2 +1,2 @@ | ||
/* eslint-disable camelcase */ | ||
export {}; | ||
//# sourceMappingURL=draco-types.js.map |
import { MeshAttribute } from '@loaders.gl/schema'; | ||
import { Schema } from '@loaders.gl/schema'; | ||
import type { DracoLoaderData } from '../draco-types'; | ||
import type { DracoLoaderData } from "../draco-types.js"; | ||
/** Extract an arrow-like schema from a Draco mesh */ | ||
@@ -5,0 +5,0 @@ export declare function getDracoSchema(attributes: { |
import { deduceMeshField } from '@loaders.gl/schema'; | ||
/** Extract an arrow-like schema from a Draco mesh */ | ||
export function getDracoSchema(attributes, loaderData, indices) { | ||
const metadata = makeMetadata(loaderData.metadata); | ||
const fields = []; | ||
const namedLoaderDataAttributes = transformAttributesLoaderData(loaderData.attributes); | ||
for (const attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
const field = getArrowFieldFromAttribute(attributeName, attribute, namedLoaderDataAttributes[attributeName]); | ||
fields.push(field); | ||
} | ||
if (indices) { | ||
const indicesField = getArrowFieldFromAttribute('indices', indices); | ||
fields.push(indicesField); | ||
} | ||
return { | ||
fields, | ||
metadata | ||
}; | ||
const metadata = makeMetadata(loaderData.metadata); | ||
const fields = []; | ||
const namedLoaderDataAttributes = transformAttributesLoaderData(loaderData.attributes); | ||
for (const attributeName in attributes) { | ||
const attribute = attributes[attributeName]; | ||
const field = getArrowFieldFromAttribute(attributeName, attribute, namedLoaderDataAttributes[attributeName]); | ||
fields.push(field); | ||
} | ||
if (indices) { | ||
const indicesField = getArrowFieldFromAttribute('indices', indices); | ||
fields.push(indicesField); | ||
} | ||
return { fields, metadata }; | ||
} | ||
function transformAttributesLoaderData(loaderData) { | ||
const result = {}; | ||
for (const key in loaderData) { | ||
const dracoAttribute = loaderData[key]; | ||
result[dracoAttribute.name || 'undefined'] = dracoAttribute; | ||
} | ||
return result; | ||
const result = {}; | ||
for (const key in loaderData) { | ||
const dracoAttribute = loaderData[key]; | ||
result[dracoAttribute.name || 'undefined'] = dracoAttribute; | ||
} | ||
return result; | ||
} | ||
function getArrowFieldFromAttribute(attributeName, attribute, loaderData) { | ||
const metadataMap = loaderData ? makeMetadata(loaderData.metadata) : undefined; | ||
const field = deduceMeshField(attributeName, attribute, metadataMap); | ||
return field; | ||
const metadataMap = loaderData ? makeMetadata(loaderData.metadata) : undefined; | ||
const field = deduceMeshField(attributeName, attribute, metadataMap); | ||
return field; | ||
} | ||
function makeMetadata(metadata) { | ||
Object.entries(metadata); | ||
const serializedMetadata = {}; | ||
for (const key in metadata) { | ||
serializedMetadata[`${key}.string`] = JSON.stringify(metadata[key]); | ||
} | ||
return serializedMetadata; | ||
Object.entries(metadata); | ||
const serializedMetadata = {}; | ||
for (const key in metadata) { | ||
serializedMetadata[`${key}.string`] = JSON.stringify(metadata[key]); | ||
} | ||
return serializedMetadata; | ||
} | ||
//# sourceMappingURL=get-draco-schema.js.map |
@@ -0,2 +1,4 @@ | ||
// Version constant cannot be imported, it needs to correspond to the build version of **this** module. | ||
// __VERSION__ is injected by babel-plugin-version-inline | ||
// @ts-ignore TS2304: Cannot find name '__VERSION__'. | ||
export const VERSION = typeof "4.2.0-alpha.4" !== 'undefined' ? "4.2.0-alpha.4" : 'latest'; | ||
//# sourceMappingURL=version.js.map |
@@ -0,1 +1,2 @@ | ||
// Polyfills increases the bundle size significantly. Use it for NodeJS worker only | ||
import '@loaders.gl/polyfills'; | ||
@@ -5,2 +6,1 @@ import { createLoaderWorker } from '@loaders.gl/loader-utils'; | ||
createLoaderWorker(DracoLoader); | ||
//# sourceMappingURL=draco-worker-node.js.map |
import { createLoaderWorker } from '@loaders.gl/loader-utils'; | ||
import { DracoLoader } from "../index.js"; | ||
createLoaderWorker(DracoLoader); | ||
//# sourceMappingURL=draco-worker.js.map |
@@ -0,1 +1,2 @@ | ||
// Polyfills increases the bundle size significantly. Use it for NodeJS worker only | ||
import '@loaders.gl/polyfills'; | ||
@@ -5,28 +6,22 @@ import { WorkerBody } from '@loaders.gl/worker-utils'; | ||
(async () => { | ||
if (!(await WorkerBody.inWorkerThread())) { | ||
return; | ||
} | ||
WorkerBody.onmessage = async (type, payload) => { | ||
switch (type) { | ||
case 'process': | ||
try { | ||
const { | ||
input, | ||
options | ||
} = payload; | ||
const result = await DracoWriter.encode(input, options); | ||
WorkerBody.postMessage('done', { | ||
result | ||
}); | ||
} catch (error) { | ||
const message = error instanceof Error ? error.message : ''; | ||
WorkerBody.postMessage('error', { | ||
error: message | ||
}); | ||
// Check that we are actually in a worker thread | ||
if (!(await WorkerBody.inWorkerThread())) { | ||
return; | ||
} | ||
WorkerBody.onmessage = async (type, payload) => { | ||
switch (type) { | ||
case 'process': | ||
try { | ||
const { input, options } = payload; | ||
const result = await DracoWriter.encode(input, options); | ||
WorkerBody.postMessage('done', { result }); | ||
} | ||
catch (error) { | ||
const message = error instanceof Error ? error.message : ''; | ||
WorkerBody.postMessage('error', { error: message }); | ||
} | ||
break; | ||
default: | ||
} | ||
break; | ||
default: | ||
} | ||
}; | ||
}; | ||
})(); | ||
//# sourceMappingURL=draco-writer-worker-node.js.map |
import { WorkerBody } from '@loaders.gl/worker-utils'; | ||
import { DracoWriter } from "../draco-writer.js"; | ||
(async () => { | ||
if (!(await WorkerBody.inWorkerThread())) { | ||
return; | ||
} | ||
WorkerBody.onmessage = async (type, payload) => { | ||
switch (type) { | ||
case 'process': | ||
try { | ||
const { | ||
input, | ||
options | ||
} = payload; | ||
const result = await DracoWriter.encode(input, options); | ||
WorkerBody.postMessage('done', { | ||
result | ||
}); | ||
} catch (error) { | ||
const message = error instanceof Error ? error.message : ''; | ||
WorkerBody.postMessage('error', { | ||
error: message | ||
}); | ||
// Check that we are actually in a worker thread | ||
if (!(await WorkerBody.inWorkerThread())) { | ||
return; | ||
} | ||
WorkerBody.onmessage = async (type, payload) => { | ||
switch (type) { | ||
case 'process': | ||
try { | ||
const { input, options } = payload; | ||
const result = await DracoWriter.encode(input, options); | ||
WorkerBody.postMessage('done', { result }); | ||
} | ||
catch (error) { | ||
const message = error instanceof Error ? error.message : ''; | ||
WorkerBody.postMessage('error', { error: message }); | ||
} | ||
break; | ||
default: | ||
} | ||
break; | ||
default: | ||
} | ||
}; | ||
}; | ||
})(); | ||
//# sourceMappingURL=draco-writer-worker.js.map |
{ | ||
"name": "@loaders.gl/draco", | ||
"version": "4.2.0-alpha.4", | ||
"version": "4.2.0-alpha.5", | ||
"description": "Framework-independent loader and writer for Draco compressed meshes and point clouds", | ||
@@ -49,5 +49,6 @@ "license": "MIT", | ||
"scripts": { | ||
"pre-build": "npm run copy-libs && npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-workers", | ||
"pre-build": "npm run copy-libs && npm run build-bundle && npm run build-bundle-dev && npm run build-workers", | ||
"copy-libs": "cp -rf ./src/libs ./dist/libs", | ||
"build-bundle": "ocular-bundle ./src/index.ts", | ||
"build-bundle": "ocular-bundle ./bundle.ts --output=dist/dist.min.js", | ||
"build-bundle-dev": "ocular-bundle ./bundle.ts --env=dev --output=dist/dist.dev.js", | ||
"build-workers": "yarn build-loader-worker && yarn build-loader-worker-node && yarn build-writer-worker && yarn build-writer-worker-node", | ||
@@ -60,12 +61,14 @@ "build-loader-worker": "esbuild src/workers/draco-worker.ts --outfile=dist/draco-worker.js --target=esnext --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"", | ||
"dependencies": { | ||
"@babel/runtime": "^7.3.1", | ||
"@loaders.gl/loader-utils": "4.2.0-alpha.4", | ||
"@loaders.gl/schema": "4.2.0-alpha.4", | ||
"@loaders.gl/worker-utils": "4.2.0-alpha.4", | ||
"@loaders.gl/loader-utils": "4.2.0-alpha.5", | ||
"@loaders.gl/schema": "4.2.0-alpha.5", | ||
"@loaders.gl/worker-utils": "4.2.0-alpha.5", | ||
"draco3d": "1.5.5" | ||
}, | ||
"devDependencies": { | ||
"@loaders.gl/polyfills": "4.2.0-alpha.4" | ||
"@loaders.gl/polyfills": "4.2.0-alpha.5" | ||
}, | ||
"gitHead": "6c52dee5c3f005648a394cc4aee7fc37005c8e83" | ||
"peerDependencies": { | ||
"@loaders.gl/core": "^4.0.0" | ||
}, | ||
"gitHead": "32d95a81971f104e4dfeb88ab57065f05321a76a" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
6
10
24
14294605
77
146227
+ Added@loaders.gl/core@4.3.3(transitive)
+ Added@loaders.gl/loader-utils@4.2.0-alpha.54.3.3(transitive)
+ Added@loaders.gl/schema@4.2.0-alpha.54.3.3(transitive)
+ Added@loaders.gl/worker-utils@4.2.0-alpha.54.3.3(transitive)
+ Added@probe.gl/env@4.1.0(transitive)
+ Added@probe.gl/log@4.1.0(transitive)
- Removed@babel/runtime@^7.3.1
- Removed@babel/runtime@7.26.9(transitive)
- Removed@loaders.gl/loader-utils@4.2.0-alpha.4(transitive)
- Removed@loaders.gl/schema@4.2.0-alpha.4(transitive)
- Removed@loaders.gl/worker-utils@4.2.0-alpha.4(transitive)
- Removedregenerator-runtime@0.14.1(transitive)