Files
XgridConverter/app/workers/converter.worker.ts
2026-03-09 14:37:59 +01:00

183 lines
5.0 KiB
TypeScript

const originalFetch = globalThis.fetch;
globalThis.fetch = async (input, init) => {
const url = input instanceof Request ? input.url : input.toString();
self.postMessage({ type: "LOG", message: `FETCH: ${url}` });
if (url.includes("webp.wasm")) {
self.postMessage({
type: "LOG",
message: `INTERCEPTED → /workers/webp.wasm`,
});
const res = await originalFetch("/workers/webp.wasm", init);
self.postMessage({
type: "LOG",
message: `WASM response status: ${res.status}`,
});
return res;
}
return originalFetch(input, init);
};
// Intercept XMLHttpRequest (Emscripten uses this in Workers)
if (typeof XMLHttpRequest !== "undefined") {
const originalOpen = XMLHttpRequest.prototype.open;
// @ts-ignore
XMLHttpRequest.prototype.open = function (
method: string,
url: string | URL,
...rest: any[]
) {
if (typeof url === "string" && url.includes("webp.wasm")) {
url = "/workers/webp.wasm";
}
return originalOpen.apply(this, [method, url, ...rest] as any);
};
}
self.onmessage = async (e: MessageEvent) => {
const { type, filesData, mainLccName, fileName } = e.data;
if (type === "START_CONVERSION") {
try {
self.postMessage({ type: "LOG", message: "Initialisiere..." });
// Emscripten's native locateFile hook
// @ts-ignore
globalThis.Module = globalThis.Module || {};
// @ts-ignore
globalThis.Module.locateFile = function (path: string) {
if (path.endsWith(".wasm")) {
return new URL("/webp.wasm", self.location.origin).href;
}
return path;
};
const {
readFile,
writeFile,
MemoryReadFileSystem,
MemoryFileSystem,
getInputFormat,
} = await import("@playcanvas/splat-transform");
const readFs = new MemoryReadFileSystem();
self.postMessage({
type: "LOG",
message: "Lade Dateien in den virtuellen Speicher...",
});
for (const file of filesData) {
readFs.set(file.name, new Uint8Array(file.buffer));
}
const readOptions = {
iterations: 0,
lodSelect: [0, 1, 2, 3, 4], // we have captured a total level of 5
unbundled: false,
lodChunkCount: 0,
lodChunkExtent: 0,
};
self.postMessage({ type: "LOG", message: "Lese LCC und Binärdaten..." });
const tables = await readFile({
filename: mainLccName,
fileSystem: readFs,
inputFormat: getInputFormat(mainLccName),
params: [],
options: readOptions,
});
const mainTable = tables[0];
if (!mainTable) throw new Error("Keine Splat-Daten gefunden.");
const generatedFiles: { name: string; blob: Blob }[] = [];
// PASS 1: Generate Single High-Quality SOG
self.postMessage({ type: "LOG", message: "Kompiliere Single SOG..." });
const writeFsSingle = new MemoryFileSystem();
const singleOutputName = `${fileName}.sog`;
const singleOptions = {
...readOptions,
iterations: 10,
unbundled: false,
};
await writeFile(
{
filename: singleOutputName,
outputFormat: "sog-bundle",
dataTable: mainTable,
options: singleOptions,
},
writeFsSingle,
);
const singleSogData = writeFsSingle.results.get(singleOutputName);
if (singleSogData) {
generatedFiles.push({
name: singleOutputName,
blob: new Blob([new Uint8Array(singleSogData).slice().buffer], {
type: "application/octet-stream",
}),
});
}
// ==========================================
// PASS 2: Generate Unbundled LOD SOGs + JSON
// ==========================================
self.postMessage({ type: "LOG", message: "Kompiliere LOD Chunks..." });
const writeFsLods = new MemoryFileSystem();
// MUST be exactly "meta.json" for unbundled SOG format
const lodsOutputName = "meta.json";
const lodOptions = {
...readOptions,
iterations: 10,
unbundled: true,
lodChunkCount: 512,
lodChunkExtent: 16,
};
await writeFile(
{
filename: lodsOutputName,
outputFormat: "sog",
dataTable: mainTable,
options: lodOptions,
},
writeFsLods,
);
// Jetzt iterieren wir über alle generierten Dateien im System
for (const [generatedName, data] of writeFsLods.results.entries()) {
const mimeType = generatedName.endsWith(".json")
? "application/json"
: "application/octet-stream";
generatedFiles.push({
name: generatedName,
blob: new Blob([new Uint8Array(data).slice().buffer], {
type: mimeType,
}),
});
}
// Send all Data to Frontend
self.postMessage({
type: "DONE",
data: {
files: generatedFiles,
},
});
} catch (err: any) {
self.postMessage({ type: "LOG", message: `Fehler: ${err.message}` });
console.error(err);
}
}
};