Initial project import
This commit is contained in:
342
node_modules/three-stdlib/loaders/DRACOLoader.js
generated
vendored
Normal file
342
node_modules/three-stdlib/loaders/DRACOLoader.js
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
import { Loader, FileLoader, BufferGeometry, BufferAttribute } from "three";
|
||||
const _taskCache = /* @__PURE__ */ new WeakMap();
|
||||
class DRACOLoader extends 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 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 BufferGeometry();
|
||||
if (geometryData.index) {
|
||||
geometry.setIndex(new 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 BufferAttribute(array, itemSize));
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
_loadLibrary(url, responseType) {
|
||||
const loader = new 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((module) => {
|
||||
const draco = module.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
export {
|
||||
DRACOLoader
|
||||
};
|
||||
//# sourceMappingURL=DRACOLoader.js.map
|
||||
Reference in New Issue
Block a user