Files
yawn/addons/gltf-import/src/index.js
T

63 lines
2.1 KiB
JavaScript

/** Fetches and parses glTF in a worker, then lets that worker write the packet into Yawn's SAB arena. */
export class GltfImporter {
#core;
#worker;
#next = 1;
#pending = new Map();
constructor(core, { workerFactory } = {}) {
if (!core?.allocateRows) throw new TypeError("core must be a YawnCore instance");
this.#core = core;
this.#worker = workerFactory?.() ?? new Worker(new URL("./worker.js", import.meta.url), {
type: "module",
name: "yawn-gltf-import",
});
this.#worker.addEventListener("message", ({ data }) => this.#message(data));
this.#worker.addEventListener("error", () => this.#fail("GLTF_WORKER_ERROR"));
this.#worker.start?.();
}
load(url) {
const source = url instanceof URL ? url.href : url;
if (typeof source !== "string" || !source) throw new TypeError("url is required");
const request = this.#next++;
return new Promise((resolve, reject) => {
this.#pending.set(request, { resolve, reject });
this.#worker.postMessage({ type: "load", request, url: source });
});
}
async #message(message) {
const pending = this.#pending.get(message?.request);
if (!pending) return;
try {
if (message.type === "allocate") {
pending.array = await this.#core.allocateRows({
name: `gltf.${message.request}`,
rows: Math.ceil(message.byteLength / 16),
stride: 16,
format: "u32",
});
this.#worker.postMessage({ type: "storage", request: message.request, ...pending.array.share() });
} else {
this.#pending.delete(message.request);
if (message.type === "ready") pending.resolve({ array: pending.array, byteLength: message.byteLength });
else pending.reject(new Error(message.error ?? "GLTF_IMPORT_FAILED"));
}
} catch (error) {
this.#pending.delete(message.request);
pending.reject(error);
}
}
#fail(code) {
for (const { reject } of this.#pending.values()) reject(new Error(code));
this.#pending.clear();
}
dispose() {
this.#fail("DISPOSED");
this.#worker.terminate();
}
}