110 lines
3.0 KiB
JavaScript
110 lines
3.0 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
};
|