"use strict"; (() => { // ../worker-utils/src/lib/env-utils/version.ts 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." ); globalThis._loadersgl_.version = NPM_TAG; } else { globalThis._loadersgl_.version = "4.3.1"; } } return globalThis._loadersgl_.version; } var VERSION = getVersion(); // ../worker-utils/src/lib/env-utils/assert.ts function assert(condition, message) { if (!condition) { throw new Error(message || "loaders.gl assertion failed."); } } // ../worker-utils/src/lib/env-utils/globals.ts var globals = { self: typeof self !== "undefined" && self, window: typeof window !== "undefined" && window, global: typeof global !== "undefined" && global, document: typeof document !== "undefined" && document }; var self_ = globals.self || globals.window || globals.global || {}; var window_ = globals.window || globals.self || globals.global || {}; var global_ = globals.global || globals.self || globals.window || {}; var document_ = globals.document || {}; var isBrowser = ( // @ts-ignore process.browser typeof process !== "object" || String(process) !== "[object process]" || process.browser ); var isWorker = typeof importScripts === "function"; var isMobile = typeof window !== "undefined" && typeof window.orientation !== "undefined"; var matches = typeof process !== "undefined" && process.version && /v([0-9]*)/.exec(process.version); var nodeVersion = matches && parseFloat(matches[1]) || 0; // ../worker-utils/src/lib/node/worker_threads-browser.ts var parentPort = null; // ../worker-utils/src/lib/worker-utils/get-transfer-list.ts function getTransferList(object, recursive = true, transfers) { const transfersSet = transfers || /* @__PURE__ */ new Set(); if (!object) { } else if (isTransferable(object)) { transfersSet.add(object); } else if (isTransferable(object.buffer)) { transfersSet.add(object.buffer); } else if (ArrayBuffer.isView(object)) { } else if (recursive && typeof object === "object") { for (const key in object) { getTransferList(object[key], recursive, transfersSet); } } return transfers === void 0 ? Array.from(transfersSet) : []; } function isTransferable(object) { if (!object) { return false; } if (object instanceof ArrayBuffer) { return true; } if (typeof MessagePort !== "undefined" && object instanceof MessagePort) { return true; } if (typeof ImageBitmap !== "undefined" && object instanceof ImageBitmap) { return true; } if (typeof OffscreenCanvas !== "undefined" && object instanceof OffscreenCanvas) { return true; } return false; } // ../worker-utils/src/lib/worker-farm/worker-body.ts async function getParentPort() { return parentPort; } var onMessageWrapperMap = /* @__PURE__ */ new Map(); var WorkerBody = class { /** Check that we are actually in a worker thread */ static async inWorkerThread() { return typeof self !== "undefined" || Boolean(await getParentPort()); } /* * (type: WorkerMessageType, payload: WorkerMessagePayload) => any */ static set onmessage(onMessage) { async function handleMessage(message) { const parentPort2 = await getParentPort(); const { type, payload } = parentPort2 ? message : message.data; onMessage(type, payload); } getParentPort().then((parentPort2) => { if (parentPort2) { parentPort2.on("message", (message) => { handleMessage(message); }); parentPort2.on("exit", () => console.debug("Node worker closing")); } else { globalThis.onmessage = handleMessage; } }); } static async addEventListener(onMessage) { let onMessageWrapper = onMessageWrapperMap.get(onMessage); if (!onMessageWrapper) { onMessageWrapper = async (message) => { if (!isKnownMessage(message)) { return; } const parentPort3 = await getParentPort(); const { type, payload } = parentPort3 ? message : message.data; onMessage(type, payload); }; } const parentPort2 = await getParentPort(); if (parentPort2) { console.error("not implemented"); } else { globalThis.addEventListener("message", onMessageWrapper); } } static async removeEventListener(onMessage) { const onMessageWrapper = onMessageWrapperMap.get(onMessage); onMessageWrapperMap.delete(onMessage); const parentPort2 = await getParentPort(); if (parentPort2) { console.error("not implemented"); } else { globalThis.removeEventListener("message", onMessageWrapper); } } /** * Send a message from a worker to creating thread (main thread) * @param type * @param payload */ static async postMessage(type, payload) { const data = { source: "loaders.gl", type, payload }; const transferList = getTransferList(payload); const parentPort2 = await getParentPort(); if (parentPort2) { parentPort2.postMessage(data, transferList); } else { globalThis.postMessage(data, transferList); } } }; function isKnownMessage(message) { const { type, data } = message; return type === "message" && data && typeof data.source === "string" && data.source.startsWith("loaders.gl"); } // ../worker-utils/src/lib/library-utils/library-utils.ts var loadLibraryPromises = {}; async function loadLibrary(libraryUrl, moduleName = null, options = {}, libraryName = null) { if (moduleName) { libraryUrl = getLibraryUrl(libraryUrl, moduleName, options, libraryName); } loadLibraryPromises[libraryUrl] = // eslint-disable-next-line @typescript-eslint/no-misused-promises loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl); return await loadLibraryPromises[libraryUrl]; } function getLibraryUrl(library, moduleName, options = {}, libraryName = null) { if (!options.useLocalLibraries && library.startsWith("http")) { return library; } libraryName = libraryName || library; const modules = options.modules || {}; if (modules[libraryName]) { return modules[libraryName]; } if (!isBrowser) { return `modules/${moduleName}/dist/libs/${libraryName}`; } if (options.CDN) { assert(options.CDN.startsWith("http")); return `${options.CDN}/${moduleName}@${VERSION}/dist/libs/${libraryName}`; } if (isWorker) { return `../src/libs/${libraryName}`; } return `modules/${moduleName}/src/libs/${libraryName}`; } async function loadLibraryFromFile(libraryUrl) { if (libraryUrl.endsWith("wasm")) { return await loadAsArrayBuffer(libraryUrl); } if (!isBrowser) { try { const { requireFromFile } = globalThis.loaders || {}; return await requireFromFile?.(libraryUrl); } catch (error) { console.error(error); return null; } } if (isWorker) { return importScripts(libraryUrl); } const scriptSource = await loadAsText(libraryUrl); return loadLibraryFromString(scriptSource, libraryUrl); } function loadLibraryFromString(scriptSource, id) { if (!isBrowser) { const { requireFromString } = globalThis.loaders || {}; return requireFromString?.(scriptSource, id); } if (isWorker) { eval.call(globalThis, scriptSource); return null; } const script = document.createElement("script"); script.id = id; try { script.appendChild(document.createTextNode(scriptSource)); } catch (e) { script.text = scriptSource; } document.body.appendChild(script); return null; } async function loadAsArrayBuffer(url) { const { readFileAsArrayBuffer } = globalThis.loaders || {}; if (isBrowser || !readFileAsArrayBuffer || url.startsWith("http")) { const response = await fetch(url); return await response.arrayBuffer(); } return await readFileAsArrayBuffer(url); } async function loadAsText(url) { const { readFileAsText } = globalThis.loaders || {}; if (isBrowser || !readFileAsText || url.startsWith("http")) { const response = await fetch(url); return await response.text(); } return await readFileAsText(url); } // src/lib/draco-builder.ts var GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = { POSITION: "POSITION", NORMAL: "NORMAL", COLOR_0: "COLOR", TEXCOORD_0: "TEX_COORD" }; var noop = () => { }; var DracoBuilder = class { draco; dracoEncoder; dracoMeshBuilder; dracoMetadataBuilder; log; // 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(); } destroy() { this.destroyEncodedObject(this.dracoMeshBuilder); this.destroyEncodedObject(this.dracoEncoder); this.destroyEncodedObject(this.dracoMetadataBuilder); this.dracoMeshBuilder = null; this.dracoEncoder = null; this.draco = null; } // TBD - when does this need to be called? destroyEncodedObject(object) { if (object) { this.draco.destroy(object); } } /** * Encode mesh or point cloud * @param mesh =({}) * @param options */ encodeSync(mesh, options = {}) { this.log = noop; this._setOptions(options); return options.pointcloud ? this._encodePointCloud(mesh, options) : this._encodeMesh(mesh, options); } // PRIVATE _getAttributesFromMesh(mesh) { const attributes = { ...mesh, ...mesh.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); 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); } } _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 with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`); return dracoInt8ArrayToArrayBuffer(dracoData); } finally { this.destroyEncodedObject(dracoData); this.destroyEncodedObject(dracoMesh); } } /** * Set encoding options. * @param {{speed?: any; method?: any; quantization?: any;}} options */ _setOptions(options) { if ("speed" in options) { this.dracoEncoder.SetSpeedOptions(...options.speed); } if ("method" in options) { const dracoMethod = this.draco[options.method || "MESH_SEQUENTIAL_ENCODING"]; 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); } } } /** * @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; } /** * @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; } /** * @param mesh * @param attributeName * @param attribute * @param vertexCount */ _addAttributeToMesh(mesh, attributeName, attribute, vertexCount) { if (!ArrayBuffer.isView(attribute)) { return -1; } 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; } 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: return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer)); default: console.warn("Unsupported attribute type", attribute); return -1; } } /** * 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; } } _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; } /** * 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); } /** * 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 ); } /** * 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); } } } }; 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; } function getEntries(container) { const hasEntriesFunc = container.entries && !container.hasOwnProperty("entries"); return hasEntriesFunc ? container.entries() : Object.entries(container); } // src/lib/draco-module-loader.ts var DRACO_DECODER_VERSION = "1.5.6"; var DRACO_ENCODER_VERSION = "1.4.1"; var STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`; 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" }; var 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}` }; var loadEncoderPromise; async function loadDracoEncoderModule(options) { const modules = options.modules || {}; if (modules.draco3d) { loadEncoderPromise ||= modules.draco3d.createEncoderModule({}).then((draco) => { return { draco }; }); } else { loadEncoderPromise ||= loadDracoEncoder(options); } return await loadEncoderPromise; } 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 }) // Module is Promise-like. Wrap in object to avoid loop. }); }); } // src/lib/utils/version.ts var VERSION2 = true ? "4.3.1" : "latest"; // src/draco-writer.ts var DEFAULT_DRACO_WRITER_OPTIONS = { 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 // } }; var DracoWriter = { name: "DRACO", id: "draco", module: "draco", version: VERSION2, extensions: ["drc"], options: { draco: DEFAULT_DRACO_WRITER_OPTIONS }, encode }; async function encode(data, options = {}) { const { draco } = await loadDracoEncoderModule(options); const dracoBuilder = new DracoBuilder(draco); try { return dracoBuilder.encodeSync(data, options.draco); } finally { dracoBuilder.destroy(); } } // src/workers/draco-writer-worker.ts (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 }); } break; default: } }; })(); })(); //# sourceMappingURL=draco-writer-worker.js.map