gaussian-splat-lite 1.4.2 → 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.
- package/CHANGELOG.md +45 -1
- package/CONTRIBUTING.md +1 -1
- package/README.md +1 -1
- package/bin/build-lod.js +56 -5
- package/bin/decoder/cluster.wasm +0 -0
- package/bin/rad/cluster.js +756 -0
- package/bin/rad/parallel.js +84 -6
- package/bin/rad/rad.js +107 -123
- package/bin/rad/splats.js +27 -10
- package/bin/rad/storage.js +4 -1
- package/dist/gaussian-splat-lite.cjs +777 -609
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +126 -126
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +777 -609
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +3391 -3254
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/dist/types/data/IndexedSplats.d.cts +12 -4
- package/dist/types/data/IndexedSplats.d.ts +12 -4
- package/dist/types/data/RadPagedSplats.d.cts +3 -23
- package/dist/types/data/RadPagedSplats.d.ts +3 -23
- package/dist/types/data/Splats.d.cts +4 -6
- package/dist/types/data/Splats.d.ts +4 -6
- package/dist/types/data/defines.d.cts +5 -4
- package/dist/types/data/defines.d.ts +5 -4
- package/dist/types/data/pendingUploads.d.cts +14 -0
- package/dist/types/data/pendingUploads.d.ts +14 -0
- package/dist/types/data/splatData.d.cts +1 -1
- package/dist/types/data/splatData.d.ts +1 -1
- package/dist/types/data/splatRange.d.cts +1 -1
- package/dist/types/data/splatRange.d.ts +1 -1
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/loaders/SplatLoader.d.cts +2 -0
- package/dist/types/loaders/SplatLoader.d.ts +2 -0
- package/dist/types/loaders/loadSplatData.d.cts +3 -0
- package/dist/types/loaders/loadSplatData.d.ts +3 -0
- package/dist/types/loaders/loadTypes.d.cts +3 -0
- package/dist/types/loaders/loadTypes.d.ts +3 -0
- package/dist/types/loaders/morton.d.cts +6 -3
- package/dist/types/loaders/morton.d.ts +6 -3
- package/dist/types/loaders/postDecode/protocol.d.cts +1 -0
- package/dist/types/loaders/postDecode/protocol.d.ts +1 -0
- package/dist/types/loaders/rad.d.cts +5 -5
- package/dist/types/loaders/rad.d.ts +5 -5
- package/dist/types/loaders/stream/rad-stream/RadSelectionState.d.cts +2 -2
- package/dist/types/loaders/stream/rad-stream/RadSelectionState.d.ts +2 -2
- package/dist/types/loaders/stream/rad-stream/RadStreamLoader.d.cts +42 -16
- package/dist/types/loaders/stream/rad-stream/RadStreamLoader.d.ts +42 -16
- package/dist/types/loaders/stream/rad-stream/RadStreamScheduler.d.cts +6 -5
- package/dist/types/loaders/stream/rad-stream/RadStreamScheduler.d.ts +6 -5
- package/dist/types/loaders/stream/rad-stream/radFade.d.cts +2 -2
- package/dist/types/loaders/stream/rad-stream/radFade.d.ts +2 -2
- package/dist/types/loaders/stream/rad-stream/radLod.d.cts +11 -3
- package/dist/types/loaders/stream/rad-stream/radLod.d.ts +11 -3
- package/dist/types/loaders/stream/rad-stream/workerHandlers.d.cts +9 -3
- package/dist/types/loaders/stream/rad-stream/workerHandlers.d.ts +9 -3
- package/dist/types/loaders/stream/sog-stream/SogStreamBatch.d.cts +0 -1
- package/dist/types/loaders/stream/sog-stream/SogStreamBatch.d.ts +0 -1
- package/dist/types/loaders/stream/sog-stream/SogStreamLoader.d.cts +21 -5
- package/dist/types/loaders/stream/sog-stream/SogStreamLoader.d.ts +21 -5
- package/dist/types/loaders/stream/sog-stream/SogStreamScheduler.d.cts +21 -4
- package/dist/types/loaders/stream/sog-stream/SogStreamScheduler.d.ts +21 -4
- package/dist/types/loaders/stream/sog-stream/sogLod.d.cts +13 -11
- package/dist/types/loaders/stream/sog-stream/sogLod.d.ts +13 -11
- package/dist/types/loaders/stream/sog-stream/sogVisibility.d.cts +13 -7
- package/dist/types/loaders/stream/sog-stream/sogVisibility.d.ts +13 -7
- package/dist/types/loaders/stream/sog-stream/workerHandlers.d.cts +8 -13
- package/dist/types/loaders/stream/sog-stream/workerHandlers.d.ts +8 -13
- package/dist/types/loaders/stream/streamOptions.d.cts +9 -3
- package/dist/types/loaders/stream/streamOptions.d.ts +9 -3
- package/dist/types/scene/SplatMesh.d.cts +5 -4
- package/dist/types/scene/SplatMesh.d.ts +5 -4
- package/docs/BuildRad.md +23 -1
- package/docs/RadStreamScheduler.md +34 -9
- package/docs/SogStreamScheduler.md +47 -11
- package/docs/SplatFileType.md +1 -1
- package/docs/SplatLoader.md +1 -0
- package/docs/SplatMesh.md +5 -4
- package/docs/Splats.md +2 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,49 @@ 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
|
+
|
|
33
|
+
## [1.4.3] - 2026-10-25
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Added `--within-dist <x,y,z,radius>` to `gsl-build-lod` to keep splat centers inside or on a sphere in input coordinates before building LOD. See [Building RAD files](docs/BuildRad.md).
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Streamed SOG keeps surrounding regions in its detail budget, with less detail behind the camera and corrected distances for oversized regions. Added runtime-adjustable `lodBehindPenalty` (default `4`) and `lodDistanceShrink` (default `0.75`).
|
|
42
|
+
|
|
43
|
+
### Removed
|
|
44
|
+
|
|
45
|
+
- Removed raycast hit `sourceIndex`, `Splats.getSourceIndex()`, and `RadStreamScheduler.getGlobalIndex()`. Use `hit.object.splats.getSplat(hit.index)` to read the current hit; streamed indices can change when the visible detail changes.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Streamed SOG waits for initial detail selection before rejecting `firstRenderable`, including when an empty environment finishes last.
|
|
50
|
+
- Streamed SOG rejects `firstRenderable` when all required loads fail permanently, even while the camera keeps moving.
|
|
51
|
+
- Streamed SOG retains displayed coverage when detail selection fails, stopping further loading while existing fades finish.
|
|
52
|
+
|
|
10
53
|
## [1.4.2] - 2026-10-24
|
|
11
54
|
|
|
12
55
|
### Changed
|
|
@@ -660,7 +703,8 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
660
703
|
|
|
661
704
|
- Initial public release of the Three.js Gaussian Splatting renderer.
|
|
662
705
|
|
|
663
|
-
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.4.
|
|
706
|
+
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.4.3...HEAD
|
|
707
|
+
[1.4.3]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.4.2...v1.4.3
|
|
664
708
|
[1.4.2]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.4.1...v1.4.2
|
|
665
709
|
[1.4.1]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.4.0...v1.4.1
|
|
666
710
|
[1.4.0]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.3.2...v1.4.0
|
package/CONTRIBUTING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Development setup
|
|
4
4
|
|
|
5
|
-
Requires Node.js
|
|
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
|
|
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";
|
|
@@ -24,6 +25,11 @@ Output: <input-stem>-lod.rad. Existing files are not overwritten.
|
|
|
24
25
|
Options:
|
|
25
26
|
--lod-base <base> LOD spacing (default: 1.75, range: 1.1..16)
|
|
26
27
|
--max-sh <0..3> Maximum SH degree (default: 3)
|
|
28
|
+
--within-dist <x,y,z,radius>
|
|
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
|
|
27
33
|
--rad-chunked Write a .rad header and separate .radc files
|
|
28
34
|
--memory-budget <GiB> Target memory in GiB (default: 4, minimum: 1)
|
|
29
35
|
--temp-dir <path> Temporary file location (default: system temp)
|
|
@@ -32,13 +38,14 @@ Options:
|
|
|
32
38
|
|
|
33
39
|
SH4 inputs are accepted; coefficients above --max-sh are discarded.
|
|
34
40
|
The memory target covers the whole conversion, worker threads included.
|
|
35
|
-
No compilation is required.`;
|
|
41
|
+
Requires Node.js 22.12 or later. No compilation is required.`;
|
|
36
42
|
|
|
37
43
|
function parse(args) {
|
|
38
44
|
const options = {
|
|
39
45
|
inputs: [],
|
|
40
46
|
lodBase: 1.75,
|
|
41
47
|
maxSh: 3,
|
|
48
|
+
clusterSh: 10,
|
|
42
49
|
budget: 4 * GIB,
|
|
43
50
|
chunked: false,
|
|
44
51
|
};
|
|
@@ -53,6 +60,19 @@ function parse(args) {
|
|
|
53
60
|
options.chunked = true;
|
|
54
61
|
continue;
|
|
55
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
|
+
}
|
|
56
76
|
if (!arg.startsWith("-")) {
|
|
57
77
|
options.inputs.push(arg);
|
|
58
78
|
continue;
|
|
@@ -62,6 +82,7 @@ function parse(args) {
|
|
|
62
82
|
const key = {
|
|
63
83
|
"--lod-base": "lodBase",
|
|
64
84
|
"--max-sh": "maxSh",
|
|
85
|
+
"--within-dist": "withinDist",
|
|
65
86
|
"--memory-budget": "budget",
|
|
66
87
|
"--temp-dir": "tempDir",
|
|
67
88
|
"-o": "output",
|
|
@@ -86,6 +107,16 @@ function parse(args) {
|
|
|
86
107
|
options.maxSh > 3
|
|
87
108
|
)
|
|
88
109
|
throw new Error("--max-sh must be an integer between 0 and 3");
|
|
110
|
+
if (options.withinDist !== undefined) {
|
|
111
|
+
const values = options.withinDist
|
|
112
|
+
.split(",")
|
|
113
|
+
.map((value) => (value.trim() ? Number(value) : Number.NaN));
|
|
114
|
+
if (values.length !== 4 || !values.every(Number.isFinite) || values[3] < 0)
|
|
115
|
+
throw new Error(
|
|
116
|
+
"--within-dist must be x,y,z,radius with finite numbers and a nonnegative radius",
|
|
117
|
+
);
|
|
118
|
+
options.withinDist = values;
|
|
119
|
+
}
|
|
89
120
|
if (
|
|
90
121
|
!Number.isFinite(options.budget) ||
|
|
91
122
|
options.budget < GIB ||
|
|
@@ -100,7 +131,7 @@ function parse(args) {
|
|
|
100
131
|
return options;
|
|
101
132
|
}
|
|
102
133
|
|
|
103
|
-
async function convert(input, options, cancelled) {
|
|
134
|
+
async function convert(input, options, kernel, cancelled) {
|
|
104
135
|
const output =
|
|
105
136
|
options.output ??
|
|
106
137
|
path.join(
|
|
@@ -120,7 +151,7 @@ async function convert(input, options, cancelled) {
|
|
|
120
151
|
const splats = await readInput(input, options.maxSh, workspace);
|
|
121
152
|
const count = splats.length;
|
|
122
153
|
pool = new Pool(workspace, count);
|
|
123
|
-
splats.retain(pool);
|
|
154
|
+
splats.retain(pool, options.withinDist);
|
|
124
155
|
console.log(
|
|
125
156
|
`Read ${count} splats, SH${splats.inputSh}, in ${((performance.now() - start) / 1000).toFixed(2)}s`,
|
|
126
157
|
);
|
|
@@ -132,8 +163,10 @@ async function convert(input, options, cancelled) {
|
|
|
132
163
|
console.log(
|
|
133
164
|
`Removed ${splats.invalid} splats with NaN or infinite values or a zero rotation`,
|
|
134
165
|
);
|
|
135
|
-
const empty = count - splats.invalid - splats.length;
|
|
166
|
+
const empty = count - splats.invalid - splats.cropped - splats.length;
|
|
136
167
|
if (empty) console.log(`Removed ${empty} empty splats`);
|
|
168
|
+
if (splats.cropped)
|
|
169
|
+
console.log(`Removed ${splats.cropped} splats outside --within-dist`);
|
|
137
170
|
if (!splats.length) throw new Error("Input contains no usable splats");
|
|
138
171
|
const leaves = splats.length;
|
|
139
172
|
const lodStart = performance.now();
|
|
@@ -142,11 +175,24 @@ async function convert(input, options, cancelled) {
|
|
|
142
175
|
console.log(
|
|
143
176
|
`Built ${splats.length} LOD splats from ${leaves} leaves in ${((performance.now() - lodStart) / 1000).toFixed(2)}s`,
|
|
144
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
|
+
}
|
|
145
190
|
const bytes = await writeRad(
|
|
146
191
|
splats,
|
|
147
192
|
output,
|
|
148
193
|
options.chunked,
|
|
149
194
|
options.lodBase,
|
|
195
|
+
codebook,
|
|
150
196
|
);
|
|
151
197
|
console.log(
|
|
152
198
|
`Wrote ${(bytes / 1048576).toFixed(2)} MiB in ${((performance.now() - start) / 1000).toFixed(2)}s total`,
|
|
@@ -158,11 +204,13 @@ async function convert(input, options, cancelled) {
|
|
|
158
204
|
}
|
|
159
205
|
|
|
160
206
|
async function run(options, cancelled) {
|
|
207
|
+
// Load the requested codebook kernel before converting any inputs.
|
|
208
|
+
const kernel = options.clusterSh === undefined ? undefined : codebookKernel();
|
|
161
209
|
let failed = 0;
|
|
162
210
|
for (const input of options.inputs) {
|
|
163
211
|
if (Atomics.load(cancelled, 0)) return;
|
|
164
212
|
try {
|
|
165
|
-
await convert(input, options, cancelled);
|
|
213
|
+
await convert(input, options, kernel, cancelled);
|
|
166
214
|
} catch (error) {
|
|
167
215
|
if (Atomics.load(cancelled, 0)) return;
|
|
168
216
|
console.error(`${input}: ${error.message ?? error}`);
|
|
@@ -231,6 +279,9 @@ try {
|
|
|
231
279
|
console[method] = (...args) => parentPort.postMessage({ method, args });
|
|
232
280
|
await run(workerData.options, workerData.cancelled);
|
|
233
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");
|
|
234
285
|
const options = parse(process.argv.slice(2));
|
|
235
286
|
if (!options) console.log(help);
|
|
236
287
|
else await start(options);
|
|
Binary file
|