PC SOGS decoding from .Zip archive (#100)

* Implement PC SOGS decoding from .zip file archive. Update viewer and editor to accept .zip files.

* Restore GL WEBP decoding.
This commit is contained in:
Andreas Sundquist
2025-07-07 17:18:11 -07:00
committed by GitHub
parent cd40e139d1
commit 4266db3259
5 changed files with 136 additions and 12 deletions
+55 -1
View File
@@ -1,3 +1,4 @@
import { unzipSync } from "fflate";
import { FileLoader, Loader, type LoadingManager } from "three";
import { PackedSplats } from "./PackedSplats";
import { SplatMesh } from "./SplatMesh";
@@ -138,6 +139,7 @@ export enum SplatFileType {
SPLAT = "splat",
KSPLAT = "ksplat",
PCSOGS = "pcsogs",
PCSOGSZIP = "pcsogszip",
}
export function getSplatFileType(
@@ -157,6 +159,14 @@ export function getSplatFileType(
// Unknown Gzipped file type
return undefined;
}
if (view.getUint32(0, true) === 0x04034b50) {
// PKZip file
if (tryPcSogsZip(fileBytes)) {
return SplatFileType.PCSOGSZIP;
}
// Unknown PKZip file type
return undefined;
}
// Unknown file type
return undefined;
}
@@ -218,7 +228,7 @@ export type PcSogsJson = {
maxs: number[];
files: string[];
};
shN: {
shN?: {
shape: number[];
dtype: string;
mins: number;
@@ -277,6 +287,37 @@ export function tryPcSogs(
}
}
export function tryPcSogsZip(
input: ArrayBuffer | Uint8Array,
): { name: string; json: PcSogsJson } | undefined {
try {
const fileBytes =
input instanceof ArrayBuffer ? new Uint8Array(input) : input;
let metaFilename: string | null = null;
const unzipped = unzipSync(fileBytes, {
filter: ({ name }) => {
const filename = name.split(/[\\/]/).pop() as string;
if (filename === "meta.json") {
metaFilename = name;
return true;
}
return false;
},
});
if (!metaFilename) {
return undefined;
}
const json = tryPcSogs(unzipped[metaFilename]);
if (!json) {
return undefined;
}
return { name: metaFilename, json };
} catch {
return undefined;
}
}
export async function unpackSplats({
input,
extraFiles,
@@ -373,6 +414,19 @@ export async function unpackSplats({
return { packedArray, numSplats, extra };
});
}
case SplatFileType.PCSOGSZIP: {
return await withWorker(async (worker) => {
const { packedArray, numSplats, extra } = (await worker.call(
"decodePcSogsZip",
{ fileBytes },
)) as {
packedArray: Uint32Array;
numSplats: number;
extra: Record<string, unknown>;
};
return { packedArray, numSplats, extra };
});
}
default: {
throw new Error(`Unknown splat file type: ${splatFileType}`);
}