three-stdlib
Version:
stand-alone library of threejs examples
343 lines (342 loc) • 12.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const _taskCache = /* @__PURE__ */ new WeakMap();
class DRACOLoader extends THREE.Loader {
constructor(manager) {
super(manager);
this.decoderPath = "";
this.decoderConfig = {};
this.decoderBinary = null;
this.decoderPending = null;
this.workerLimit = 4;
this.workerPool = [];
this.workerNextTaskID = 1;
this.workerSourceURL = "";
this.defaultAttributeIDs = {
position: "POSITION",
normal: "NORMAL",
color: "COLOR",
uv: "TEX_COORD"
};
this.defaultAttributeTypes = {
position: "Float32Array",
normal: "Float32Array",
color: "Float32Array",
uv: "Float32Array"
};
}
setDecoderPath(path) {
this.decoderPath = path;
return this;
}
setDecoderConfig(config) {
this.decoderConfig = config;
return this;
}
setWorkerLimit(workerLimit) {
this.workerLimit = workerLimit;
return this;
}
load(url, onLoad, onProgress, onError) {
const loader = new THREE.FileLoader(this.manager);
loader.setPath(this.path);
loader.setResponseType("arraybuffer");
loader.setRequestHeader(this.requestHeader);
loader.setWithCredentials(this.withCredentials);
loader.load(
url,
(buffer) => {
const taskConfig = {
attributeIDs: this.defaultAttributeIDs,
attributeTypes: this.defaultAttributeTypes,
useUniqueIDs: false
};
this.decodeGeometry(buffer, taskConfig).then(onLoad).catch(onError);
},
onProgress,
onError
);
}
/** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
decodeDracoFile(buffer, callback, attributeIDs, attributeTypes) {
const taskConfig = {
attributeIDs: attributeIDs || this.defaultAttributeIDs,
attributeTypes: attributeTypes || this.defaultAttributeTypes,
useUniqueIDs: !!attributeIDs
};
this.decodeGeometry(buffer, taskConfig).then(callback);
}
decodeGeometry(buffer, taskConfig) {
for (const attribute in taskConfig.attributeTypes) {
const type = taskConfig.attributeTypes[attribute];
if (type.BYTES_PER_ELEMENT !== void 0) {
taskConfig.attributeTypes[attribute] = type.name;
}
}
const taskKey = JSON.stringify(taskConfig);
if (_taskCache.has(buffer)) {
const cachedTask = _taskCache.get(buffer);
if (cachedTask.key === taskKey) {
return cachedTask.promise;
} else if (buffer.byteLength === 0) {
throw new Error(
"THREE.DRACOLoader: Unable to re-decode a buffer with different settings. Buffer has already been transferred."
);
}
}
let worker;
const taskID = this.workerNextTaskID++;
const taskCost = buffer.byteLength;
const geometryPending = this._getWorker(taskID, taskCost).then((_worker) => {
worker = _worker;
return new Promise((resolve, reject) => {
worker._callbacks[taskID] = { resolve, reject };
worker.postMessage({ type: "decode", id: taskID, taskConfig, buffer }, [buffer]);
});
}).then((message) => this._createGeometry(message.geometry));
geometryPending.catch(() => true).then(() => {
if (worker && taskID) {
this._releaseTask(worker, taskID);
}
});
_taskCache.set(buffer, {
key: taskKey,
promise: geometryPending
});
return geometryPending;
}
_createGeometry(geometryData) {
const geometry = new THREE.BufferGeometry();
if (geometryData.index) {
geometry.setIndex(new THREE.BufferAttribute(geometryData.index.array, 1));
}
for (let i = 0; i < geometryData.attributes.length; i++) {
const attribute = geometryData.attributes[i];
const name = attribute.name;
const array = attribute.array;
const itemSize = attribute.itemSize;
geometry.setAttribute(name, new THREE.BufferAttribute(array, itemSize));
}
return geometry;
}
_loadLibrary(url, responseType) {
const loader = new THREE.FileLoader(this.manager);
loader.setPath(this.decoderPath);
loader.setResponseType(responseType);
loader.setWithCredentials(this.withCredentials);
return new Promise((resolve, reject) => {
loader.load(url, resolve, void 0, reject);
});
}
preload() {
this._initDecoder();
return this;
}
_initDecoder() {
if (this.decoderPending)
return this.decoderPending;
const useJS = typeof WebAssembly !== "object" || this.decoderConfig.type === "js";
const librariesPending = [];
if (useJS) {
librariesPending.push(this._loadLibrary("draco_decoder.js", "text"));
} else {
librariesPending.push(this._loadLibrary("draco_wasm_wrapper.js", "text"));
librariesPending.push(this._loadLibrary("draco_decoder.wasm", "arraybuffer"));
}
this.decoderPending = Promise.all(librariesPending).then((libraries) => {
const jsContent = libraries[0];
if (!useJS) {
this.decoderConfig.wasmBinary = libraries[1];
}
const fn = DRACOWorker.toString();
const body = [
"/* draco decoder */",
jsContent,
"",
"/* worker */",
fn.substring(fn.indexOf("{") + 1, fn.lastIndexOf("}"))
].join("\n");
this.workerSourceURL = URL.createObjectURL(new Blob([body]));
});
return this.decoderPending;
}
_getWorker(taskID, taskCost) {
return this._initDecoder().then(() => {
if (this.workerPool.length < this.workerLimit) {
const worker2 = new Worker(this.workerSourceURL);
worker2._callbacks = {};
worker2._taskCosts = {};
worker2._taskLoad = 0;
worker2.postMessage({ type: "init", decoderConfig: this.decoderConfig });
worker2.onmessage = function(e) {
const message = e.data;
switch (message.type) {
case "decode":
worker2._callbacks[message.id].resolve(message);
break;
case "error":
worker2._callbacks[message.id].reject(message);
break;
default:
console.error('THREE.DRACOLoader: Unexpected message, "' + message.type + '"');
}
};
this.workerPool.push(worker2);
} else {
this.workerPool.sort(function(a, b) {
return a._taskLoad > b._taskLoad ? -1 : 1;
});
}
const worker = this.workerPool[this.workerPool.length - 1];
worker._taskCosts[taskID] = taskCost;
worker._taskLoad += taskCost;
return worker;
});
}
_releaseTask(worker, taskID) {
worker._taskLoad -= worker._taskCosts[taskID];
delete worker._callbacks[taskID];
delete worker._taskCosts[taskID];
}
debug() {
console.log(
"Task load: ",
this.workerPool.map((worker) => worker._taskLoad)
);
}
dispose() {
for (let i = 0; i < this.workerPool.length; ++i) {
this.workerPool[i].terminate();
}
this.workerPool.length = 0;
return this;
}
}
function DRACOWorker() {
let decoderConfig;
let decoderPending;
onmessage = function(e) {
const message = e.data;
switch (message.type) {
case "init":
decoderConfig = message.decoderConfig;
decoderPending = new Promise(function(resolve) {
decoderConfig.onModuleLoaded = function(draco) {
resolve({ draco });
};
DracoDecoderModule(decoderConfig);
});
break;
case "decode":
const buffer = message.buffer;
const taskConfig = message.taskConfig;
decoderPending.then((module2) => {
const draco = module2.draco;
const decoder = new draco.Decoder();
const decoderBuffer = new draco.DecoderBuffer();
decoderBuffer.Init(new Int8Array(buffer), buffer.byteLength);
try {
const geometry = decodeGeometry(draco, decoder, decoderBuffer, taskConfig);
const buffers = geometry.attributes.map((attr) => attr.array.buffer);
if (geometry.index)
buffers.push(geometry.index.array.buffer);
self.postMessage({ type: "decode", id: message.id, geometry }, buffers);
} catch (error) {
console.error(error);
self.postMessage({ type: "error", id: message.id, error: error.message });
} finally {
draco.destroy(decoderBuffer);
draco.destroy(decoder);
}
});
break;
}
};
function decodeGeometry(draco, decoder, decoderBuffer, taskConfig) {
const attributeIDs = taskConfig.attributeIDs;
const attributeTypes = taskConfig.attributeTypes;
let dracoGeometry;
let decodingStatus;
const geometryType = decoder.GetEncodedGeometryType(decoderBuffer);
if (geometryType === draco.TRIANGULAR_MESH) {
dracoGeometry = new draco.Mesh();
decodingStatus = decoder.DecodeBufferToMesh(decoderBuffer, dracoGeometry);
} else if (geometryType === draco.POINT_CLOUD) {
dracoGeometry = new draco.PointCloud();
decodingStatus = decoder.DecodeBufferToPointCloud(decoderBuffer, dracoGeometry);
} else {
throw new Error("THREE.DRACOLoader: Unexpected geometry type.");
}
if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
throw new Error("THREE.DRACOLoader: Decoding failed: " + decodingStatus.error_msg());
}
const geometry = { index: null, attributes: [] };
for (const attributeName in attributeIDs) {
const attributeType = self[attributeTypes[attributeName]];
let attribute;
let attributeID;
if (taskConfig.useUniqueIDs) {
attributeID = attributeIDs[attributeName];
attribute = decoder.GetAttributeByUniqueId(dracoGeometry, attributeID);
} else {
attributeID = decoder.GetAttributeId(dracoGeometry, draco[attributeIDs[attributeName]]);
if (attributeID === -1)
continue;
attribute = decoder.GetAttribute(dracoGeometry, attributeID);
}
geometry.attributes.push(decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute));
}
if (geometryType === draco.TRIANGULAR_MESH) {
geometry.index = decodeIndex(draco, decoder, dracoGeometry);
}
draco.destroy(dracoGeometry);
return geometry;
}
function decodeIndex(draco, decoder, dracoGeometry) {
const numFaces = dracoGeometry.num_faces();
const numIndices = numFaces * 3;
const byteLength = numIndices * 4;
const ptr = draco._malloc(byteLength);
decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
const index = new Uint32Array(draco.HEAPF32.buffer, ptr, numIndices).slice();
draco._free(ptr);
return { array: index, itemSize: 1 };
}
function decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute) {
const numComponents = attribute.num_components();
const numPoints = dracoGeometry.num_points();
const numValues = numPoints * numComponents;
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
const dataType = getDracoDataType(draco, attributeType);
const ptr = draco._malloc(byteLength);
decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr);
const array = new attributeType(draco.HEAPF32.buffer, ptr, numValues).slice();
draco._free(ptr);
return {
name: attributeName,
array,
itemSize: numComponents
};
}
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;
}
}
}
exports.DRACOLoader = DRACOLoader;
//# sourceMappingURL=DRACOLoader.cjs.map