Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/lod-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ The most important options are:
- `--quality`: Use the higher-quality, slower `bhatt-lod` method. Recommended for offline LoD tree building and streaming.
- `--max-sh=#`: Limit the maximum Spherical Harmonics encoded, from 0..3.
- `--rad-chunked`: Output a chunked RAD file for streaming, with .RAD header and .RADC chunk files.
- `--zstd` (`--zstd-level=#`, default 9): Compress the RAD property blobs with zstd instead of deflate. On scenes with third-band Spherical Harmonics this writes a file around 25% smaller that also decodes about twice as fast, which matters most when streaming; a scene without SH gains little. Note that a zstd RAD file cannot be read by Spark versions older than this option.

When using `--rad-chunked` the resulting files will be a small header file `my-splats-lod.rad` and chunks in `my-splats-lod-0.radc`, `...-lod-1.radc`, etc. Use the `my-splats-lod.rad` file as URL with `paged: true` and Spark will automatically fetch the chunks as needed.

Expand Down
102 changes: 99 additions & 3 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ itertools = "0.14.0"
js-sys = "0.3.77"
miniz_oxide = "0.8.9"
ordered-float = "5.1.0"
# zstd .rad property-blob decode: libzstd through the `zstd` crate, compiled into
# the wasm. Verified to link and run on wasm32. NOTE: the BUILD needs a clang that
# can target wasm32; consumers of the published dist need no toolchain.
zstd = "0.13"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
serde-wasm-bindgen = "0.6.5"
Expand Down
29 changes: 28 additions & 1 deletion rust/build-lod/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::io::{BufReader, BufWriter, Read, Write};

use spark_lib::{chunk_tree, sh_clustering};
use spark_lib::decoder::{SplatEncoding, SplatGetter, SplatReceiver};
use spark_lib::rad::RadEncoder;
use spark_lib::rad::{RadChunkPropertyCompression, RadEncoder};
use spark_lib::{
decoder::{ChunkReceiver, MultiDecoder},
gsplat::GsplatArray,
Expand Down Expand Up @@ -64,6 +64,8 @@ struct BuildLodOptions {
within_dist: Option<([f32; 3], f32)>,
skip_validate: bool,
inflate: bool,
zstd: bool,
zstd_level: i32,
cluster_sh: Option<usize>,
cluster_sh_cpu: bool,
cluster_sh_f16: Option<bool>,
Expand Down Expand Up @@ -320,6 +322,13 @@ fn process_file_lod_tsplat<TS: SplatReceiver + TsplatArray + SplatGetter>(filena
if let Some(sh_clusters) = sh_clusters {
encoder = encoder.with_sh_clusters(sh_clusters);
}
if options.zstd {
let level = if options.zstd_level > 0 { options.zstd_level } else { 9 };
encoder = encoder
.with_compression(RadChunkPropertyCompression::Zstd)
.with_zstd_level(level);
println!("Using zstd compression (level {})", level);
}

let input_encoding = serde_json::json!({
"center": encoder.center_encoding,
Expand Down Expand Up @@ -414,6 +423,7 @@ fn show_usage_exit() {
eprintln!(" [--within-dist=<x>,<y>,<z>,<radius>] // Crop input file to within radius of a point");
eprintln!(" [--skip-validate] // Skip validation of input file");
eprintln!(" [--inflate] // Inflate scales to output normal splat opacity 0..1");
eprintln!(" [--zstd] [--zstd-level=<n>] // Compress .rad property blobs with zstd (default level 9) instead of gz");
eprintln!(" [--cluster-sh[=<iterations>]] // Cluster SH coefficients into <=64K codebook (default 10 iterations)");
eprintln!(" [--cluster-sh-cpu[=<iterations>]] // Cluster SH coefficients using CPU");
eprintln!(" [--cluster-sh-f16[=auto,true,false]] // Force GPU SH coefficients to use float16 (default if available)");
Expand Down Expand Up @@ -564,6 +574,23 @@ fn main() {
println!("Using --inflate: Inflate scales to output normal splat opacity 0..1");
continue;
}
if arg == "--zstd" {
options.zstd = true;
continue;
}
if let Some(rest) = arg.strip_prefix("--zstd-level=") {
match rest.parse::<i32>() {
Ok(v) => {
options.zstd = true;
options.zstd_level = v;
}
Err(_) => {
eprintln!("Invalid --zstd-level value: {}", rest);
show_usage_exit();
}
}
continue;
}
if let Some(rest) = arg.strip_prefix("--cluster-sh-cpu") {
options.cluster_sh_cpu = true;
if let Some(rest) = rest.strip_prefix("=") {
Expand Down
1 change: 1 addition & 0 deletions rust/spark-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ image = { workspace = true, optional = true }
hnsw.workspace = true
rand_pcg.workspace = true
space.workspace = true
zstd.workspace = true
Loading