import { WebPCodec, readFile, writeFile, MemoryReadFileSystem, MemoryFileSystem, } from "@playcanvas/splat-transform"; // 1. Configure WASM path for the browser environment WebPCodec.config({ wasmUrl: "/webp.wasm" }); /** * Converts Xgrids LCI binary data to a standard PLY * Parses 12-byte float chunks (X, Y, Z) from the binary buffer. */ function convertLciToPly(lciBuffer) { const view = new DataView(lciBuffer); // Xgrids LCI files usually start with a 4-byte point count const numPoints = view.getUint32(0, true); const points = []; let offset = 4; for (let i = 0; i < numPoints; i++) { if (offset + 12 > lciBuffer.byteLength) break; const x = view.getFloat32(offset, true); const y = view.getFloat32(offset + 4, true); const z = view.getFloat32(offset + 8, true); points.push(`${x} ${y} ${z}`); offset += 12; } const header = [ "ply", "format ascii 1.0", `element vertex ${points.length}`, "property float x", "property float y", "property float z", "end_header", ].join("\n"); return header + "\n" + points.join("\n"); } self.onmessage = async (e) => { const { type, lccBuffer, lciBuffer, fileName } = e.data; if (type === "START_CONVERSION") { try { // --- STEP 1: GENERATE COLLISION MESH (.PLY) --- self.postMessage({ type: "LOG", message: "Parsing LCI Geometry..." }); const plyData = convertLciToPly(lciBuffer); const plyBlob = new Blob([plyData], { type: "text/plain" }); // --- STEP 2: GENERATE HIGHEST QUALITY .SOG --- self.postMessage({ type: "LOG", message: "Loading LCC into Splat Engine...", }); const readFs = new MemoryReadFileSystem(); readFs.add(`${fileName}.lcc`, new Uint8Array(lccBuffer)); // splat-transform handles LCC containers natively const dataTable = await readFile(`${fileName}.lcc`, { fs: readFs }); self.postMessage({ type: "LOG", message: "Compiling High Quality SOG (WASM)...", }); const writeFsSog = new MemoryFileSystem(); await writeFile(dataTable, `${fileName}.sog`, { fs: writeFsSog }); const sogBlob = new Blob([writeFsSog.get(`${fileName}.sog`)]); // --- STEP 3: GENERATE LODs (UNBUNDLED TILES) --- self.postMessage({ type: "LOG", message: "Generating LOD Tiled Structure...", }); const writeFsLod = new MemoryFileSystem(); // 'unbundled' creates the tiled WebP/JSON structure for streaming await writeFile(dataTable, "lods/meta.json", { fs: writeFsLod, unbundled: true, }); // --- STEP 4: RETURN ALL ASSETS --- self.postMessage({ type: "DONE", data: { sog: sogBlob, ply: plyBlob, lods: writeFsLod.files, // This contains the meta.json and .webp tiles fileName: fileName, }, }); } catch (err) { self.postMessage({ type: "LOG", message: `CRITICAL ERROR: ${err.message}`, }); console.error(err); } } };