gaussian-splat-lite 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +1 -1
  4. package/bin/build-lod.js +39 -3
  5. package/bin/decoder/cluster.wasm +0 -0
  6. package/bin/rad/cluster.js +756 -0
  7. package/bin/rad/parallel.js +77 -0
  8. package/bin/rad/rad.js +107 -123
  9. package/bin/rad/storage.js +4 -1
  10. package/dist/gaussian-splat-lite.cjs +685 -551
  11. package/dist/gaussian-splat-lite.cjs.map +1 -1
  12. package/dist/gaussian-splat-lite.min.cjs +118 -118
  13. package/dist/gaussian-splat-lite.min.cjs.map +1 -1
  14. package/dist/gaussian-splat-lite.module.js +685 -551
  15. package/dist/gaussian-splat-lite.module.js.map +1 -1
  16. package/dist/gaussian-splat-lite.module.min.js +3335 -3217
  17. package/dist/gaussian-splat-lite.module.min.js.map +1 -1
  18. package/dist/types/data/IndexedSplats.d.cts +11 -0
  19. package/dist/types/data/IndexedSplats.d.ts +11 -0
  20. package/dist/types/data/RadPagedSplats.d.cts +3 -23
  21. package/dist/types/data/RadPagedSplats.d.ts +3 -23
  22. package/dist/types/data/Splats.d.cts +2 -0
  23. package/dist/types/data/Splats.d.ts +2 -0
  24. package/dist/types/data/defines.d.cts +2 -0
  25. package/dist/types/data/defines.d.ts +2 -0
  26. package/dist/types/data/pendingUploads.d.cts +14 -0
  27. package/dist/types/data/pendingUploads.d.ts +14 -0
  28. package/dist/types/index.d.cts +2 -0
  29. package/dist/types/index.d.ts +2 -0
  30. package/dist/types/loaders/SplatLoader.d.cts +2 -0
  31. package/dist/types/loaders/SplatLoader.d.ts +2 -0
  32. package/dist/types/loaders/loadSplatData.d.cts +3 -0
  33. package/dist/types/loaders/loadSplatData.d.ts +3 -0
  34. package/dist/types/loaders/loadTypes.d.cts +3 -0
  35. package/dist/types/loaders/loadTypes.d.ts +3 -0
  36. package/dist/types/loaders/stream/rad-stream/RadSelectionState.d.cts +2 -2
  37. package/dist/types/loaders/stream/rad-stream/RadSelectionState.d.ts +2 -2
  38. package/dist/types/loaders/stream/rad-stream/RadStreamLoader.d.cts +42 -16
  39. package/dist/types/loaders/stream/rad-stream/RadStreamLoader.d.ts +42 -16
  40. package/dist/types/loaders/stream/rad-stream/RadStreamScheduler.d.cts +6 -3
  41. package/dist/types/loaders/stream/rad-stream/RadStreamScheduler.d.ts +6 -3
  42. package/dist/types/loaders/stream/rad-stream/radFade.d.cts +2 -2
  43. package/dist/types/loaders/stream/rad-stream/radFade.d.ts +2 -2
  44. package/dist/types/loaders/stream/rad-stream/radLod.d.cts +11 -3
  45. package/dist/types/loaders/stream/rad-stream/radLod.d.ts +11 -3
  46. package/dist/types/loaders/stream/rad-stream/workerHandlers.d.cts +9 -3
  47. package/dist/types/loaders/stream/rad-stream/workerHandlers.d.ts +9 -3
  48. package/dist/types/loaders/stream/sog-stream/SogStreamBatch.d.cts +0 -1
  49. package/dist/types/loaders/stream/sog-stream/SogStreamBatch.d.ts +0 -1
  50. package/dist/types/loaders/stream/sog-stream/SogStreamLoader.d.cts +20 -4
  51. package/dist/types/loaders/stream/sog-stream/SogStreamLoader.d.ts +20 -4
  52. package/dist/types/loaders/stream/sog-stream/SogStreamScheduler.d.cts +5 -2
  53. package/dist/types/loaders/stream/sog-stream/SogStreamScheduler.d.ts +5 -2
  54. package/dist/types/loaders/stream/sog-stream/sogLod.d.cts +5 -2
  55. package/dist/types/loaders/stream/sog-stream/sogLod.d.ts +5 -2
  56. package/dist/types/loaders/stream/sog-stream/workerHandlers.d.cts +8 -13
  57. package/dist/types/loaders/stream/sog-stream/workerHandlers.d.ts +8 -13
  58. package/dist/types/loaders/stream/streamOptions.d.cts +8 -2
  59. package/dist/types/loaders/stream/streamOptions.d.ts +8 -2
  60. package/dist/types/scene/SplatMesh.d.cts +5 -2
  61. package/dist/types/scene/SplatMesh.d.ts +5 -2
  62. package/docs/BuildRad.md +22 -1
  63. package/docs/RadStreamScheduler.md +30 -5
  64. package/docs/SogStreamScheduler.md +33 -7
  65. package/docs/SplatFileType.md +1 -1
  66. package/docs/SplatLoader.md +1 -0
  67. package/docs/SplatMesh.md +3 -2
  68. package/docs/Splats.md +1 -0
  69. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.5.0] - 2026-10-27
11
+
12
+ ### Added
13
+
14
+ - Added `sources` to `RadStreamScheduler` and `SogStreamScheduler` to stream several inputs with one detail selection. The inputs share the splat budget and loading concurrency; RAD inputs also share both memory budgets, and each uses as much memory per Splat as the highest SH degree among them, limited by `maxSh`. See [RAD streaming](docs/RadStreamScheduler.md#multiple-inputs) and [SOG streaming](docs/SogStreamScheduler.md#multiple-inputs).
15
+ - Added an `environment` option to `SogStreamScheduler` and each of its `sources`. Set it to `false` to skip that input's environment.
16
+ - Added `maxSh` to `RadStreamScheduler` and `SogStreamScheduler` to limit SH decoding, storage, and rendering. Set it at creation to `0`–`3` (default `3`) to reduce SH memory use; `0` keeps only the base color. RAD page budgets account for the reduced storage, and the built-in SOG loader skips higher-order SH assets at `0`.
17
+ - Added a loading-time `maxSh` option to `SplatMesh` and `Splats`, and a `SplatLoader.maxSh` setting (`0`–`4`, default `4`). PLY, SPZ, SOG, and RAD loads omit higher SH bands from CPU arrays and GPU source textures.
18
+ - Added default SH codebook compression to `gsl-build-lod`: up to 65,536 shared SH entries and a 16-bit index per splat. Compression can reduce file size at the cost of view-dependent detail; small models may grow. Use `--cluster-sh=<n>` to change the default 10 refinement iterations or `--no-cluster-sh` to store coefficients directly. Clustering uses CPU workers within the memory target. See [SH codebook](docs/BuildRad.md#sh-codebook).
19
+
20
+ ### Changed
21
+
22
+ - Raised the minimum Node.js version to 22.12 for the package and `gsl-build-lod`.
23
+ - `SplatMesh.maxSh` is now read-only and set through its constructor options, matching the streaming schedulers. Assigning `mesh.maxSh` now throws a `TypeError` in strict-mode code, including ES modules. Create a new mesh to change the limit. The viewer's Max SH control has been removed.
24
+ - RAD streaming now uses fixed 65,536-splat page slots. Smaller chunks use a full slot; chunks larger than 65,536 splats are rejected during initialization.
25
+ - RAD and SOG schedulers now create their own `THREE.Group`; the `group` constructor option has been removed. Add and transform `streaming.group` instead.
26
+ - RAD and SOG streaming upload written texture layers before their batches are first displayed. Each scheduler limits pending source texture uploads to 24 MiB until rendering submits them, preventing skipped renders from accumulating larger uploads. A larger SOG region proceeds alone.
27
+ - `gsl-build-lod` now quantizes SH in fixed `1/128` steps instead of using adaptive per-band ranges. Both codebook and direct output clip coefficients to `[-127/128, 127/128]`. With `--no-cluster-sh`, original SPZ splats retain their SH coefficients except `-1`, which clips to `-127/128`.
28
+
29
+ ### Fixed
30
+
31
+ - Fixed delayed cache memory reclamation in `gsl-build-lod` when reserving memory for worker threads.
32
+
10
33
  ## [1.4.3] - 2026-10-25
11
34
 
12
35
  ### Added
package/CONTRIBUTING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Development setup
4
4
 
5
- Requires Node.js 20.19+ or 22.12+ and Rust via `rustup`. `npm run build:wasm` installs the `wasm32-unknown-unknown` target if needed.
5
+ Requires Node.js 22.12 or later and Rust via `rustup`. `npm run build:wasm` installs the `wasm32-unknown-unknown` target if needed.
6
6
 
7
7
  ```bash
8
8
  npm ci
package/README.md CHANGED
@@ -180,7 +180,7 @@ const streaming = new SogStreamScheduler({
180
180
 
181
181
  ## Development
182
182
 
183
- Requires Node.js 20.19+ or 22.12+, Rust via `rustup`, and the `wasm32-unknown-unknown` target. `build:wasm` installs `wasm-pack` through Cargo if needed.
183
+ Requires Node.js 22.12 or later, Rust via `rustup`, and the `wasm32-unknown-unknown` target. `build:wasm` installs `wasm-pack` through Cargo if needed.
184
184
 
185
185
  ```sh
186
186
  npm ci
package/bin/build-lod.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  parentPort,
8
8
  workerData,
9
9
  } from "node:worker_threads";
10
+ import { clusterSh, codebookKernel } from "./rad/cluster.js";
10
11
  import { readInput } from "./rad/input.js";
11
12
  import { buildLod } from "./rad/lod.js";
12
13
  import { Pool } from "./rad/parallel.js";
@@ -26,6 +27,9 @@ Options:
26
27
  --max-sh <0..3> Maximum SH degree (default: 3)
27
28
  --within-dist <x,y,z,radius>
28
29
  Keep splat centers within this sphere (input coordinates)
30
+ --cluster-sh[=<n>] Store SH as a codebook of up to 65536 entries, refined
31
+ in n iterations (default: enabled, 10 iterations)
32
+ --no-cluster-sh Store SH coefficients without a codebook
29
33
  --rad-chunked Write a .rad header and separate .radc files
30
34
  --memory-budget <GiB> Target memory in GiB (default: 4, minimum: 1)
31
35
  --temp-dir <path> Temporary file location (default: system temp)
@@ -34,13 +38,14 @@ Options:
34
38
 
35
39
  SH4 inputs are accepted; coefficients above --max-sh are discarded.
36
40
  The memory target covers the whole conversion, worker threads included.
37
- No compilation is required.`;
41
+ Requires Node.js 22.12 or later. No compilation is required.`;
38
42
 
39
43
  function parse(args) {
40
44
  const options = {
41
45
  inputs: [],
42
46
  lodBase: 1.75,
43
47
  maxSh: 3,
48
+ clusterSh: 10,
44
49
  budget: 4 * GIB,
45
50
  chunked: false,
46
51
  };
@@ -55,6 +60,19 @@ function parse(args) {
55
60
  options.chunked = true;
56
61
  continue;
57
62
  }
63
+ if (arg === "--no-cluster-sh") {
64
+ options.clusterSh = undefined;
65
+ continue;
66
+ }
67
+ if (arg === "--cluster-sh" || arg.startsWith("--cluster-sh=")) {
68
+ const value = arg === "--cluster-sh" ? "10" : arg.slice(13);
69
+ options.clusterSh = value.trim() ? Number(value) : Number.NaN;
70
+ if (!Number.isSafeInteger(options.clusterSh) || options.clusterSh < 0)
71
+ throw new Error(
72
+ "--cluster-sh iterations must be a nonnegative integer",
73
+ );
74
+ continue;
75
+ }
58
76
  if (!arg.startsWith("-")) {
59
77
  options.inputs.push(arg);
60
78
  continue;
@@ -113,7 +131,7 @@ function parse(args) {
113
131
  return options;
114
132
  }
115
133
 
116
- async function convert(input, options, cancelled) {
134
+ async function convert(input, options, kernel, cancelled) {
117
135
  const output =
118
136
  options.output ??
119
137
  path.join(
@@ -157,11 +175,24 @@ async function convert(input, options, cancelled) {
157
175
  console.log(
158
176
  `Built ${splats.length} LOD splats from ${leaves} leaves in ${((performance.now() - lodStart) / 1000).toFixed(2)}s`,
159
177
  );
178
+ let codebook;
179
+ if (kernel && !splats.maxSh)
180
+ console.log("No SH coefficients to cluster; writing without a codebook");
181
+ else if (kernel) {
182
+ const clusterStart = performance.now();
183
+ codebook = await clusterSh(splats, options.clusterSh, kernel);
184
+ // RAD encoding now reads only the codebook and labels.
185
+ for (const column of splats.sh) column.dispose();
186
+ console.log(
187
+ `Clustered SH into ${codebook.count} codes in ${((performance.now() - clusterStart) / 1000).toFixed(2)}s`,
188
+ );
189
+ }
160
190
  const bytes = await writeRad(
161
191
  splats,
162
192
  output,
163
193
  options.chunked,
164
194
  options.lodBase,
195
+ codebook,
165
196
  );
166
197
  console.log(
167
198
  `Wrote ${(bytes / 1048576).toFixed(2)} MiB in ${((performance.now() - start) / 1000).toFixed(2)}s total`,
@@ -173,11 +204,13 @@ async function convert(input, options, cancelled) {
173
204
  }
174
205
 
175
206
  async function run(options, cancelled) {
207
+ // Load the requested codebook kernel before converting any inputs.
208
+ const kernel = options.clusterSh === undefined ? undefined : codebookKernel();
176
209
  let failed = 0;
177
210
  for (const input of options.inputs) {
178
211
  if (Atomics.load(cancelled, 0)) return;
179
212
  try {
180
- await convert(input, options, cancelled);
213
+ await convert(input, options, kernel, cancelled);
181
214
  } catch (error) {
182
215
  if (Atomics.load(cancelled, 0)) return;
183
216
  console.error(`${input}: ${error.message ?? error}`);
@@ -246,6 +279,9 @@ try {
246
279
  console[method] = (...args) => parentPort.postMessage({ method, args });
247
280
  await run(workerData.options, workerData.cancelled);
248
281
  } else {
282
+ const [major, minor] = process.versions.node.split(".").map(Number);
283
+ if (major < 22 || (major === 22 && minor < 12))
284
+ throw new Error("gsl-build-lod requires Node.js 22.12 or later");
249
285
  const options = parse(process.argv.slice(2));
250
286
  if (!options) console.log(help);
251
287
  else await start(options);
Binary file