From bf3d3d1204a0baeafdb98d5cd462f9eb9beffa9d Mon Sep 17 00:00:00 2001 From: Oscar Lorentzon Date: Thu, 17 Sep 2026 23:00:50 -0700 Subject: [PATCH 1/2] Move the SPZ input types to the SPZ module The loader stopped using these types when the function taking them moved away. The argument type is now exported too, so callers can name what they pass. --- src/SplatLoader.ts | 17 ----------------- src/index.ts | 2 ++ src/spz.ts | 23 ++++++++++++++++++----- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/SplatLoader.ts b/src/SplatLoader.ts index d803da54..6792b00f 100644 --- a/src/SplatLoader.ts +++ b/src/SplatLoader.ts @@ -5,7 +5,6 @@ import { PackedSplats, type PackedSplatsOptions } from "./PackedSplats"; import { SplatMesh } from "./SplatMesh"; import { workerPool } from "./SplatWorker"; import { type ExtResult, type PackedResult, SplatFileType } from "./defines"; -import type { SpzWriteVersion } from "./spz"; import { decompressPartialGzip, getTextureSize } from "./utils"; // SplatLoader implements the THREE.Loader interface and supports loading a variety @@ -625,19 +624,3 @@ export class SplatData { } } } - -export type FileInput = { - fileBytes: Uint8Array; - fileType?: SplatFileType; - pathOrUrl?: string; - transform?: { translate?: number[]; quaternion?: number[]; scale?: number }; -}; - -export type TranscodeSpzInput = { - inputs: FileInput[]; - maxSh?: number; - clipXyz?: { min: number[]; max: number[] }; - fractionalBits?: number; - opacityThreshold?: number; - version?: SpzWriteVersion; -}; diff --git a/src/index.ts b/src/index.ts index d045f5ae..24781ab2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,8 @@ export { writeSpz, type SpzWriteVersion, type WriteSpzOptions, + type TranscodeSpzFileInput, + type TranscodeSpzInput, } from "./spz"; export { PackedSplats, type PackedSplatsOptions } from "./PackedSplats"; diff --git a/src/spz.ts b/src/spz.ts index 880434e8..9ccf86ff 100644 --- a/src/spz.ts +++ b/src/spz.ts @@ -1,15 +1,28 @@ import type { PackedSplats } from "./PackedSplats"; -import { - type TranscodeSpzInput, - getSplatFileType, - getSplatFileTypeFromPath, -} from "./SplatLoader"; +import { getSplatFileType, getSplatFileTypeFromPath } from "./SplatLoader"; +import type { SplatFileType } from "./defines"; import { decode_to_gsplatarray, packedsplats_to_gsplatarray } from "spark-rs"; import * as wasm from "./wasm"; export type SpzWriteVersion = 2 | 3; +export type TranscodeSpzFileInput = { + fileBytes: Uint8Array; + fileType?: SplatFileType; + pathOrUrl?: string; + transform?: { translate?: number[]; quaternion?: number[]; scale?: number }; +}; + +export type TranscodeSpzInput = { + inputs: TranscodeSpzFileInput[]; + maxSh?: number; + clipXyz?: { min: number[]; max: number[] }; + fractionalBits?: number; + opacityThreshold?: number; + version?: SpzWriteVersion; +}; + export async function transcodeSpz(input: TranscodeSpzInput) { await wasm.initialization; From 7bd102ccb6de36995bed4f6ef8ae865978cebb1d Mon Sep 17 00:00:00 2001 From: Oscar Lorentzon Date: Thu, 17 Sep 2026 23:01:01 -0700 Subject: [PATCH 2/2] Accept extended splats when writing SPZ Only the packed container was accepted, so extended splats had to be converted first, rounding their positions. --- rust/spark-rs/src/lib.rs | 1 + src/spz.ts | 61 +++++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/rust/spark-rs/src/lib.rs b/rust/spark-rs/src/lib.rs index 3bd688ba..ee75dd3a 100644 --- a/rust/spark-rs/src/lib.rs +++ b/rust/spark-rs/src/lib.rs @@ -529,6 +529,7 @@ pub fn extsplats_to_gsplatarray(num_splats: u32, ext1: Uint32Array, ext2: Uint32 }; Ok(GsplatArray::new(splats)) } +stub_fn!(feature = "gsplat", extsplats_to_gsplatarray); #[wasm_bindgen] #[cfg(all(feature = "csplat", feature = "tiny_lod"))] diff --git a/src/spz.ts b/src/spz.ts index 9ccf86ff..03d36e22 100644 --- a/src/spz.ts +++ b/src/spz.ts @@ -1,8 +1,13 @@ -import type { PackedSplats } from "./PackedSplats"; +import { ExtSplats } from "./ExtSplats"; +import { PackedSplats } from "./PackedSplats"; import { getSplatFileType, getSplatFileTypeFromPath } from "./SplatLoader"; import type { SplatFileType } from "./defines"; -import { decode_to_gsplatarray, packedsplats_to_gsplatarray } from "spark-rs"; +import { + decode_to_gsplatarray, + extsplats_to_gsplatarray, + packedsplats_to_gsplatarray, +} from "spark-rs"; import * as wasm from "./wasm"; export type SpzWriteVersion = 2 | 3; @@ -91,36 +96,52 @@ export type WriteSpzOptions = { }; export function writeSpz( - packedSplats: PackedSplats, + splats: PackedSplats | ExtSplats, maxSh?: number, fractionalBits?: number, ): { fileBytes: Uint8Array }; export function writeSpz( - packedSplats: PackedSplats, + splats: PackedSplats | ExtSplats, options?: WriteSpzOptions, ): { fileBytes: Uint8Array }; export function writeSpz( - packedSplats: PackedSplats, + splats: PackedSplats | ExtSplats, maxShOrOptions?: number | WriteSpzOptions, fractionalBits?: number, ) { - if (!packedSplats.packedArray) { - throw new Error(""); - } const options: WriteSpzOptions = typeof maxShOrOptions === "number" ? { maxSh: maxShOrOptions, fractionalBits } : (maxShOrOptions ?? {}); - const gsplats = packedsplats_to_gsplatarray( - packedSplats.numSplats, - packedSplats.packedArray, - packedSplats.extra, - packedSplats.splatEncoding, - ); - const spzBytes = gsplats.encode_to_spz( - options.maxSh ?? 3, - options.fractionalBits ?? 12, - options.version, - ); - return { fileBytes: spzBytes }; + const shDegree = options.maxSh ?? 3; + const bits = options.fractionalBits ?? 12; + + if (splats instanceof ExtSplats) { + const gsplats = extsplats_to_gsplatarray( + splats.numSplats, + splats.extArrays[0], + splats.extArrays[1], + splats.extra, + ); + return { + fileBytes: gsplats.encode_to_spz(shDegree, bits, options.version), + }; + } + + if (splats instanceof PackedSplats) { + if (!splats.packedArray) { + throw new Error("PackedSplats has no splat data"); + } + const gsplats = packedsplats_to_gsplatarray( + splats.numSplats, + splats.packedArray, + splats.extra, + splats.splatEncoding, + ); + return { + fileBytes: gsplats.encode_to_spz(shDegree, bits, options.version), + }; + } + + throw new Error("writeSpz requires PackedSplats or ExtSplats"); }