@playcanvas/splat-transform 2.0.4 → 2.0.6
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/README.md +110 -25
- package/dist/cli.mjs +118 -91
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +67 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +67 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ splat-transform [GLOBAL] input [ACTIONS] ... output [ACTIONS]
|
|
|
69
69
|
|
|
70
70
|
## Actions
|
|
71
71
|
|
|
72
|
-
Actions can be repeated
|
|
72
|
+
Actions execute in the order specified and can be repeated. Any action may appear after any input or output file:
|
|
73
73
|
|
|
74
74
|
```none
|
|
75
75
|
-t, --translate <x,y,z> Translate Gaussians by (x, y, z)
|
|
@@ -89,17 +89,19 @@ Actions can be repeated and applied in any order:
|
|
|
89
89
|
Use n% to keep a percentage of Gaussians
|
|
90
90
|
-G, --filter-floaters [size,op,min] Remove Gaussians not contributing to any solid voxel.
|
|
91
91
|
Evaluates each Gaussian at occupied voxel centers.
|
|
92
|
-
Default: size=0.05, opacity=0.1, min=0.004 (1/255)
|
|
92
|
+
Default: size=0.05, opacity=0.1, min=0.004 (1/255).
|
|
93
|
+
Bare flag (no value) uses all defaults.
|
|
93
94
|
-D, --filter-cluster [res,op,min] Keep only the connected cluster at --seed-pos.
|
|
94
95
|
GPU-voxelizes at coarse resolution (res world units/voxel).
|
|
95
|
-
Default: res=1.0, opacity=0.999, min=0.1
|
|
96
|
+
Default: res=1.0, opacity=0.999, min=0.1.
|
|
97
|
+
Bare flag (no value) uses all defaults.
|
|
96
98
|
-p, --params <key=val,...> Pass parameters to .mjs generator script
|
|
97
|
-
-l, --lod <n>
|
|
99
|
+
-l, --lod <n> Tag the Gaussians with LOD level n (n >= 0)
|
|
98
100
|
-m, --summary Print per-column statistics to stdout
|
|
99
101
|
-M, --morton-order Reorder Gaussians by Morton code (Z-order curve)
|
|
100
102
|
```
|
|
101
103
|
|
|
102
|
-
##
|
|
104
|
+
## General Options
|
|
103
105
|
|
|
104
106
|
```none
|
|
105
107
|
-h, --help Show this help and exit
|
|
@@ -109,29 +111,80 @@ Actions can be repeated and applied in any order:
|
|
|
109
111
|
--mem Show memory usage in progress output
|
|
110
112
|
--tty Interactive bar rendering (default on a TTY; --no-tty to disable)
|
|
111
113
|
-w, --overwrite Overwrite output file if it exists
|
|
112
|
-
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## GPU Options
|
|
117
|
+
|
|
118
|
+
Used by SOG compression and GPU voxelization (`--filter-cluster`, `--filter-floaters`, `.voxel.json` output).
|
|
119
|
+
|
|
120
|
+
```none
|
|
113
121
|
-L, --list-gpus List available GPU adapters and exit
|
|
114
|
-
-g, --gpu <n|cpu>
|
|
122
|
+
-g, --gpu <n|cpu> Device for GPU operations: GPU adapter index | 'cpu'
|
|
123
|
+
('cpu' disables GPU and is incompatible with
|
|
124
|
+
GPU-only features like --filter-cluster)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## SOG Compression Options
|
|
128
|
+
|
|
129
|
+
Apply when writing `.sog`, `meta.json`, `lod-meta.json`, or `.html` outputs.
|
|
130
|
+
|
|
131
|
+
```none
|
|
132
|
+
-i, --iterations <n> Iterations for SH compression (more=better). Default: 10
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## HTML Viewer Output Options
|
|
136
|
+
|
|
137
|
+
Apply when writing `.html` outputs.
|
|
138
|
+
|
|
139
|
+
```none
|
|
115
140
|
-E, --viewer-settings <settings.json> HTML viewer settings JSON file
|
|
116
141
|
-U, --unbundled Generate unbundled HTML viewer with separate files
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> [!NOTE]
|
|
145
|
+
> See the [SuperSplat Viewer Settings Schema](https://github.com/playcanvas/supersplat-viewer?tab=readme-ov-file#settings-schema) for details on how to pass data to the `-E` option.
|
|
146
|
+
|
|
147
|
+
## LCC Input Options
|
|
148
|
+
|
|
149
|
+
Apply when reading `.lcc` files.
|
|
150
|
+
|
|
151
|
+
```none
|
|
117
152
|
-O, --lod-select <n,n,...> Comma-separated LOD levels to read from LCC input
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## LOD Output Options
|
|
156
|
+
|
|
157
|
+
Apply when writing `lod-meta.json` (multi-LOD streaming SOG bundle).
|
|
158
|
+
|
|
159
|
+
```none
|
|
118
160
|
-C, --lod-chunk-count <n> Approximate number of Gaussians per LOD chunk in K. Default: 512
|
|
119
161
|
-X, --lod-chunk-extent <n> Approximate size of an LOD chunk in world units (m). Default: 16
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Voxel Output Options
|
|
165
|
+
|
|
166
|
+
Apply when writing `.voxel.json` (sparse voxel octree for collision detection). See the [Collision Mesh Guide](guides/COLLISION.md) for a deep dive on each step and tuning.
|
|
167
|
+
|
|
168
|
+
```none
|
|
169
|
+
--voxel-params [size,opacity] Voxel size and opacity threshold. Default: 0.05,0.1
|
|
170
|
+
--voxel-external-fill [size] Seal exterior voxels via boundary flood fill (interior scenes).
|
|
171
|
+
[size] (world units) is the dilation distance applied
|
|
172
|
+
before the flood fill to bridge small wall gaps.
|
|
173
|
+
--seed-pos is used to verify the volume is enclosed at
|
|
174
|
+
the seed; the fill is skipped if the seed is reachable
|
|
175
|
+
from outside.
|
|
176
|
+
Default size: 1.6
|
|
177
|
+
--voxel-floor-fill [radius] Fill each column upward from bottom until hitting solid (exterior scenes).
|
|
123
178
|
Optional radius (world units): only patch XZ areas surrounded by floor
|
|
124
179
|
within 2*radius; large empty exterior areas are left alone.
|
|
125
180
|
Default radius: 1.6
|
|
126
|
-
--voxel-carve
|
|
181
|
+
--voxel-carve [h,r] Carve navigable space using capsule flood fill from seed.
|
|
127
182
|
Default: height=1.6, radius=0.2
|
|
128
|
-
--seed-pos <x,y,z> Seed position for voxel
|
|
129
|
-
|
|
183
|
+
--seed-pos <x,y,z> Seed position for voxel fill/carve and --filter-cluster.
|
|
184
|
+
Default: 0,0,0
|
|
185
|
+
-K, --collision-mesh [smooth|faces] Generate collision mesh (.collision.glb). Default: smooth
|
|
130
186
|
```
|
|
131
187
|
|
|
132
|
-
> [!NOTE]
|
|
133
|
-
> See the [SuperSplat Viewer Settings Schema](https://github.com/playcanvas/supersplat-viewer?tab=readme-ov-file#settings-schema) for details on how to pass data to the `-E` option.
|
|
134
|
-
|
|
135
188
|
## Examples
|
|
136
189
|
|
|
137
190
|
### Basic Operations
|
|
@@ -244,22 +297,54 @@ splat-transform gen-grid.mjs -p width=10,height=10,scale=10,color=0.1 scenes/gri
|
|
|
244
297
|
|
|
245
298
|
### Voxel Format
|
|
246
299
|
|
|
247
|
-
The voxel format stores sparse voxel octree data for collision detection. It consists of two files: `.voxel.json` (metadata) and `.voxel.bin` (binary octree data).
|
|
300
|
+
The voxel format stores sparse voxel octree data for collision detection. It consists of two files: `.voxel.json` (metadata) and `.voxel.bin` (binary octree data). Pass `-K` to also emit a `.collision.glb` mesh derived from the voxel grid.
|
|
301
|
+
|
|
302
|
+
For a step-by-step walkthrough of each option (with illustrations), see the [Collision Mesh Guide](guides/COLLISION.md).
|
|
303
|
+
|
|
304
|
+
#### Recommended pipeline
|
|
248
305
|
|
|
249
306
|
```bash
|
|
250
|
-
|
|
251
|
-
|
|
307
|
+
splat-transform input.ply \
|
|
308
|
+
--filter-cluster --seed-pos x,y,z \
|
|
309
|
+
[--voxel-external-fill | --voxel-floor-fill] [--voxel-carve] \
|
|
310
|
+
[-K [smooth|faces]] \
|
|
311
|
+
output.voxel.json
|
|
312
|
+
```
|
|
252
313
|
|
|
253
|
-
|
|
254
|
-
splat-transform --voxel-params 0.1,0.3 input.ply output.voxel.json
|
|
314
|
+
`--filter-cluster` isolates the central scene and discards stray floaters before voxelization. `--seed-pos` is shared by `--filter-cluster` and the voxel fill/carve passes — set it once to a known-walkable point inside the scene.
|
|
255
315
|
|
|
256
|
-
|
|
257
|
-
|
|
316
|
+
#### Interior scenes (rooms, indoor scans)
|
|
317
|
+
|
|
318
|
+
Use `--voxel-external-fill` to seal the void around the room interior, then `--voxel-carve` to hollow out the navigable space:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
splat-transform room.ply \
|
|
322
|
+
--filter-cluster --seed-pos 0,1,0 \
|
|
323
|
+
--voxel-external-fill --voxel-carve \
|
|
324
|
+
-K room.voxel.json
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### Exterior scenes (outdoor objects, terrain)
|
|
328
|
+
|
|
329
|
+
Use `--voxel-floor-fill` to fill the ground beneath surfaces, optionally followed by `--voxel-carve`:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
splat-transform terrain.ply \
|
|
333
|
+
--filter-cluster --seed-pos 0,0,0 \
|
|
334
|
+
--voxel-floor-fill \
|
|
335
|
+
-K terrain.voxel.json
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Other examples
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
# Voxelize with custom resolution and opacity threshold
|
|
342
|
+
splat-transform --voxel-params 0.1,0.3 input.ply output.voxel.json
|
|
258
343
|
|
|
259
|
-
#
|
|
344
|
+
# Custom carve capsule (height, radius)
|
|
260
345
|
splat-transform --seed-pos 1,0,0 --voxel-carve 2.0,0.3 input.ply output.voxel.json
|
|
261
346
|
|
|
262
|
-
#
|
|
347
|
+
# Watertight voxel-face collision mesh
|
|
263
348
|
splat-transform -K faces input.ply output.voxel.json
|
|
264
349
|
```
|
|
265
350
|
|
package/dist/cli.mjs
CHANGED
|
@@ -17494,11 +17494,11 @@ class CompressedChunk {
|
|
|
17494
17494
|
/**
|
|
17495
17495
|
* The splat-transform version (semver MAJOR.MINOR.PATCH).
|
|
17496
17496
|
*/
|
|
17497
|
-
const version = '2.0.
|
|
17497
|
+
const version = '2.0.6';
|
|
17498
17498
|
/**
|
|
17499
17499
|
* The splat-transform revision (short Git hash of HEAD at build time).
|
|
17500
17500
|
*/
|
|
17501
|
-
const revision = '
|
|
17501
|
+
const revision = 'aa3dbae';
|
|
17502
17502
|
|
|
17503
17503
|
const generatedByString = `Generated by splat-transform ${version}`;
|
|
17504
17504
|
const chunkProps = [
|
|
@@ -19486,13 +19486,17 @@ class GpuDilation {
|
|
|
19486
19486
|
// Extract: clear bufferA, dispatch extract from sparse src into bufferA.
|
|
19487
19487
|
this.dispatchClear(slot, slot.bufferA, numWords);
|
|
19488
19488
|
this.dispatchExtract(slot, minBx, minBy, minBz, outerBx, outerBy, outerBz, numXWords);
|
|
19489
|
-
//
|
|
19490
|
-
//
|
|
19491
|
-
//
|
|
19492
|
-
//
|
|
19493
|
-
// writes
|
|
19494
|
-
//
|
|
19495
|
-
|
|
19489
|
+
// Force a queue submission boundary between the extract pass (which
|
|
19490
|
+
// writes bufferA via atomicOr — bound as `storage, read_write` with
|
|
19491
|
+
// `array<atomic<u32>>`) and the dilate-X pass (which reads bufferA
|
|
19492
|
+
// bound as `storage, read` with `array<u32>`). Without this, the
|
|
19493
|
+
// atomic writes are not reliably visible to the next pass — dilation
|
|
19494
|
+
// produces empty output. Other inter-pass transitions in this
|
|
19495
|
+
// pipeline (clear→extract, dilateX→Z→Y, Y→compact) rely on automatic
|
|
19496
|
+
// intra-encoder synchronization without issue. Verified raw Dawn
|
|
19497
|
+
// synchronizes this case correctly, so the bug is somewhere in
|
|
19498
|
+
// PlayCanvas's compute dispatch / bind-group path.
|
|
19499
|
+
this.device.submit();
|
|
19496
19500
|
// X-pass: A → B
|
|
19497
19501
|
this.dispatchX(slot, slot.bufferA, slot.bufferB, numXWords, outerNy, outerNz, halfExtentXZ);
|
|
19498
19502
|
// Z-pass: B → A
|
|
@@ -20907,8 +20911,16 @@ const binIndices = (parent, lod) => {
|
|
|
20907
20911
|
*/
|
|
20908
20912
|
const writeLod = async (options, fs) => {
|
|
20909
20913
|
const { filename, iterations, createDevice, chunkCount, chunkExtent } = options;
|
|
20910
|
-
|
|
20911
|
-
|
|
20914
|
+
// Operate in PLY space so per-leaf bounds in tree.bound are in the same
|
|
20915
|
+
// coordinate frame as the SOG chunk data emitted by writeSog (which also
|
|
20916
|
+
// converts to Transform.PLY). Without this, view-dependent streaming
|
|
20917
|
+
// built on tree.bound picks the wrong chunks because the bounds are
|
|
20918
|
+
// 180°-Z-rotated relative to the splat positions inside them.
|
|
20919
|
+
// This intentionally mutates the input tables to avoid doubling peak
|
|
20920
|
+
// memory during LOD export. Callers should treat writeLod as consuming its
|
|
20921
|
+
// DataTable inputs.
|
|
20922
|
+
const dataTable = convertToSpace(options.dataTable, Transform.PLY, true);
|
|
20923
|
+
const envDataTable = options.envDataTable ? convertToSpace(options.envDataTable, Transform.PLY, true) : null;
|
|
20912
20924
|
const outputDir = dirname(filename);
|
|
20913
20925
|
// ensure top-level output folder exists
|
|
20914
20926
|
await fs.mkdir(outputDir);
|
|
@@ -26420,8 +26432,14 @@ const voxelizeToBuffer = async (bvh, gpuVoxelization, gridBounds, voxelResolutio
|
|
|
26420
26432
|
const buffer = new BlockMaskBuffer();
|
|
26421
26433
|
const bStride = numBlocksX * numBlocksY;
|
|
26422
26434
|
const batchSize = 16;
|
|
26423
|
-
|
|
26424
|
-
|
|
26435
|
+
// Caps for one mega-flush. Sized to keep a single compute dispatch's
|
|
26436
|
+
// wall-clock comfortably under Windows D3D12 TDR (~2s watchdog) on slower
|
|
26437
|
+
// GPUs without materially affecting throughput on fast GPUs (the
|
|
26438
|
+
// double-buffered slots keep the GPU fed across the extra flushes).
|
|
26439
|
+
// Output is bit-identical regardless: each batch's full Gaussian list rides
|
|
26440
|
+
// with it, and slot buffers grow on demand to accommodate any oversize batch.
|
|
26441
|
+
const MEGA_MAX_BATCHES = 256;
|
|
26442
|
+
const MEGA_MAX_INDICES = 2 * 1024 * 1024;
|
|
26425
26443
|
const maxBlocks = GpuVoxelization.MAX_BLOCKS_PER_BATCH;
|
|
26426
26444
|
const numSlots = GpuVoxelization.NUM_SLOTS;
|
|
26427
26445
|
const slotIndexArrays = [];
|
|
@@ -28227,21 +28245,32 @@ const cropToNavigable = (grid, gridBounds, voxelResolution) => {
|
|
|
28227
28245
|
return { grid, gridBounds };
|
|
28228
28246
|
}
|
|
28229
28247
|
const { minBx, minBy, minBz, maxBx, maxBy, maxBz } = navBounds;
|
|
28230
|
-
|
|
28231
|
-
|
|
28232
|
-
|
|
28233
|
-
|
|
28248
|
+
// Pad by 1 block on each side so the cropped grid retains the solid wall
|
|
28249
|
+
// blocks immediately surrounding the navigable cavity. Matches the
|
|
28250
|
+
// MARGIN = 1 pattern used by carve() before this re-crop strips it. The
|
|
28251
|
+
// collision-mesh extractors treat out-of-grid as empty, so without this
|
|
28252
|
+
// padding the mesh has holes wherever the cavity reaches the cropped
|
|
28253
|
+
// boundary; with it, the mesh extractor sees a real SOLID→EMPTY
|
|
28254
|
+
// transition at the cavity edge and emits a sealed wall there.
|
|
28255
|
+
const MARGIN = 1;
|
|
28256
|
+
const cropMinBx = Math.max(0, minBx - MARGIN);
|
|
28257
|
+
const cropMinBy = Math.max(0, minBy - MARGIN);
|
|
28258
|
+
const cropMinBz = Math.max(0, minBz - MARGIN);
|
|
28259
|
+
const cropMaxBx = Math.min(nbx, maxBx + 1 + MARGIN);
|
|
28260
|
+
const cropMaxBy = Math.min(nby, maxBy + 1 + MARGIN);
|
|
28261
|
+
const cropMaxBz = Math.min(nbz, maxBz + 1 + MARGIN);
|
|
28262
|
+
if (cropMinBx === 0 && cropMinBy === 0 && cropMinBz === 0 &&
|
|
28234
28263
|
cropMaxBx === nbx && cropMaxBy === nby && cropMaxBz === nbz) {
|
|
28235
28264
|
return { grid, gridBounds };
|
|
28236
28265
|
}
|
|
28237
28266
|
const cropBar = logger.bar('Cropping grid', grid.types.length);
|
|
28238
|
-
const croppedGrid = grid.cropTo(
|
|
28267
|
+
const croppedGrid = grid.cropTo(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz, done => cropBar.update(done));
|
|
28239
28268
|
cropBar.end();
|
|
28240
28269
|
const blockSize = 4 * voxelResolution;
|
|
28241
|
-
const croppedMin = new Vec3(gridBounds.min.x +
|
|
28270
|
+
const croppedMin = new Vec3(gridBounds.min.x + cropMinBx * blockSize, gridBounds.min.y + cropMinBy * blockSize, gridBounds.min.z + cropMinBz * blockSize);
|
|
28242
28271
|
const croppedBounds = {
|
|
28243
28272
|
min: croppedMin,
|
|
28244
|
-
max: new Vec3(croppedMin.x + (cropMaxBx -
|
|
28273
|
+
max: new Vec3(croppedMin.x + (cropMaxBx - cropMinBx) * blockSize, croppedMin.y + (cropMaxBy - cropMinBy) * blockSize, croppedMin.z + (cropMaxBz - cropMinBz) * blockSize)
|
|
28245
28274
|
};
|
|
28246
28275
|
return { grid: croppedGrid, gridBounds: croppedBounds };
|
|
28247
28276
|
};
|
|
@@ -28368,13 +28397,27 @@ const writeVoxel = async (options, fs) => {
|
|
|
28368
28397
|
gpuVoxelization = new GpuVoxelization(device);
|
|
28369
28398
|
gpuVoxelization.uploadAllGaussians(pcDataTable, extentsResult.extents);
|
|
28370
28399
|
// Align grid bounds to block boundaries BEFORE voxelization so the
|
|
28371
|
-
// block coordinates used during voxelization match what the reader
|
|
28372
|
-
//
|
|
28373
|
-
//
|
|
28400
|
+
// block coordinates used during voxelization match what the reader
|
|
28401
|
+
// expects. fillExterior and fillFloor both need a margin of empty
|
|
28402
|
+
// voxels outside the splat's tight 3-sigma extents to do their job:
|
|
28403
|
+
// fillExterior so the boundary-face flood seeds survive its dilation
|
|
28404
|
+
// (notably below the floor), fillFloor so its column walk has empty
|
|
28405
|
+
// XZ columns to convert into wall pillars and the dilation halo to
|
|
28406
|
+
// extend the floor footprint outward.
|
|
28407
|
+
//
|
|
28408
|
+
// Lateral pad combines both as `dilation_radius + 1` voxels per side.
|
|
28409
|
+
// Vertical pad is only contributed by exteriorPad — fillFloor's
|
|
28410
|
+
// dilation is XZ-only, and Y padding would extend the wall pillars
|
|
28411
|
+
// above the splat's natural ceiling and below its floor.
|
|
28374
28412
|
const exteriorPad = hasFillExterior ?
|
|
28375
28413
|
(Math.ceil(navExteriorRadius / voxelResolution) + 1) * voxelResolution :
|
|
28376
28414
|
0;
|
|
28377
|
-
|
|
28415
|
+
const floorPad = hasFloorFill ?
|
|
28416
|
+
(Math.ceil(floorFillDilation / voxelResolution) + 1) * voxelResolution :
|
|
28417
|
+
0;
|
|
28418
|
+
const padXZ = Math.max(exteriorPad, floorPad);
|
|
28419
|
+
const padY = exteriorPad;
|
|
28420
|
+
let gridBounds = alignGridBounds(bounds.min.x - padXZ, bounds.min.y - padY, bounds.min.z - padXZ, bounds.max.x + padXZ, bounds.max.y + padY, bounds.max.z + padXZ, voxelResolution);
|
|
28378
28421
|
const buffer = await voxelizeToBuffer(bvh, gpuVoxelization, gridBounds, voxelResolution, opacityCutoff);
|
|
28379
28422
|
bvh = null;
|
|
28380
28423
|
pcDataTable = null;
|
|
@@ -29051,6 +29094,17 @@ const createDevice = async (adapterName) => {
|
|
|
29051
29094
|
stencil: false
|
|
29052
29095
|
});
|
|
29053
29096
|
await graphicsDevice.createDevice();
|
|
29097
|
+
// Surface Dawn's device-lost reason directly. PlayCanvas logs this only via
|
|
29098
|
+
// Debug.warn (debug builds) and then attempts to recreate the device, which
|
|
29099
|
+
// can fail noisily (e.g. DXGI_ERROR_DEVICE_REMOVED on D3D12 TDR) and bury
|
|
29100
|
+
// the original cause. Skip the `destroyed` reason — that fires on intentional
|
|
29101
|
+
// device.destroy() during normal shutdown.
|
|
29102
|
+
// @ts-ignore - wgpu is private on WebgpuGraphicsDevice but exposed in practice
|
|
29103
|
+
graphicsDevice.wgpu?.lost?.then((info) => {
|
|
29104
|
+
if (info?.reason === 'destroyed')
|
|
29105
|
+
return;
|
|
29106
|
+
logger.error(`WebGPU device was lost: reason=${info.reason || 'unknown'}, message=${info.message || '(none)'}`);
|
|
29107
|
+
});
|
|
29054
29108
|
return graphicsDevice;
|
|
29055
29109
|
};
|
|
29056
29110
|
|
|
@@ -29706,98 +29760,71 @@ SUPPORTED INPUTS
|
|
|
29706
29760
|
SUPPORTED OUTPUTS
|
|
29707
29761
|
.ply .compressed.ply .sog meta.json lod-meta.json .glb .csv .html .voxel.json null
|
|
29708
29762
|
|
|
29709
|
-
ACTIONS (can be repeated
|
|
29763
|
+
ACTIONS (executed in order; can be repeated)
|
|
29710
29764
|
-t, --translate <x,y,z> Translate Gaussians by (x, y, z)
|
|
29711
|
-
-r, --rotate <x,y,z> Rotate Gaussians by Euler angles
|
|
29765
|
+
-r, --rotate <x,y,z> Rotate Gaussians by Euler angles, in degrees
|
|
29712
29766
|
-s, --scale <factor> Uniformly scale Gaussians by factor
|
|
29713
29767
|
-H, --filter-harmonics <0|1|2|3> Remove spherical harmonic bands > n
|
|
29714
|
-
-N, --filter-nan Remove Gaussians with NaN values and most Inf values
|
|
29715
|
-
retains +Infinity in opacity and -Infinity in scale_*
|
|
29768
|
+
-N, --filter-nan Remove Gaussians with NaN values and most Inf values
|
|
29716
29769
|
-B, --filter-box <x,y,z,X,Y,Z> Remove Gaussians outside box (min, max corners)
|
|
29717
|
-
-S, --filter-sphere <x,y,z,radius> Remove Gaussians outside sphere
|
|
29718
|
-
-V, --filter-value <name,cmp,value> Keep Gaussians where <name> <cmp> <value
|
|
29770
|
+
-S, --filter-sphere <x,y,z,radius> Remove Gaussians outside sphere
|
|
29771
|
+
-V, --filter-value <name,cmp,value> Keep Gaussians where <name> <cmp> <value>;
|
|
29719
29772
|
cmp ∈ {lt,lte,gt,gte,eq,neq}
|
|
29720
|
-
|
|
29721
|
-
|
|
29722
|
-
|
|
29723
|
-
-F, --decimate <n|n%> Simplify to n Gaussians via progressive pairwise merging
|
|
29724
|
-
Use n% to keep a percentage of Gaussians
|
|
29725
|
-
-G, --filter-floaters [size,op,min] Remove Gaussians not contributing to any solid voxel.
|
|
29726
|
-
Evaluates each Gaussian at occupied voxel centers.
|
|
29727
|
-
Default: size=0.05, opacity=0.1, min=0.004 (1/255)
|
|
29728
|
-
-D, --filter-cluster [res,op,min] Keep only the connected cluster at --seed-pos.
|
|
29729
|
-
GPU-voxelizes at coarse resolution (res world units/voxel).
|
|
29730
|
-
Default: res=1.0, opacity=0.999, min=0.1
|
|
29773
|
+
-F, --decimate <n|n%> Simplify to n (or n%) Gaussians via pairwise merging
|
|
29774
|
+
-G, --filter-floaters [size,op,min] Remove Gaussians not contributing to any solid voxel. Default: 0.05,0.1,0.004
|
|
29775
|
+
-D, --filter-cluster [res,op,min] Keep only the connected cluster at --seed-pos. Default: 1.0,0.999,0.1
|
|
29731
29776
|
-p, --params <key=val,...> Pass parameters to .mjs generator script
|
|
29732
|
-
-l, --lod <n>
|
|
29777
|
+
-l, --lod <n> Tag the Gaussians with LOD level n (n >= 0)
|
|
29733
29778
|
-m, --summary Print per-column statistics to stdout
|
|
29734
29779
|
-M, --morton-order Reorder Gaussians by Morton code (Z-order curve)
|
|
29735
29780
|
|
|
29736
|
-
|
|
29781
|
+
GENERAL
|
|
29737
29782
|
-h, --help Show this help and exit
|
|
29738
29783
|
-v, --version Show version and exit
|
|
29739
29784
|
-q, --quiet Suppress non-error output
|
|
29740
29785
|
--verbose Show debug-level diagnostics
|
|
29741
29786
|
--mem Show peak memory in progress output
|
|
29742
|
-
--tty Interactive bar rendering (
|
|
29787
|
+
--tty Interactive bar rendering (--no-tty to disable)
|
|
29743
29788
|
-w, --overwrite Overwrite output file if it exists
|
|
29744
|
-
-i, --iterations <n> Iterations for SOG SH compression (more=better). Default: 10
|
|
29745
|
-
-L, --list-gpus List available GPU adapters and exit
|
|
29746
|
-
-g, --gpu <n|cpu> Select device for SOG compression: GPU adapter index | 'cpu'
|
|
29747
|
-
-E, --viewer-settings <settings.json> HTML viewer settings JSON file
|
|
29748
|
-
-U, --unbundled Generate unbundled HTML viewer with separate files
|
|
29749
|
-
-O, --lod-select <n,n,...> Comma-separated LOD levels to read from LCC input
|
|
29750
|
-
-C, --lod-chunk-count <n> Approximate number of Gaussians per LOD chunk in K. Default: 512
|
|
29751
|
-
-X, --lod-chunk-extent <n> Approximate size of an LOD chunk in world units (m). Default: 16
|
|
29752
|
-
--voxel-params [size,opacity] Voxel size and opacity threshold for .voxel.json. Default: 0.05,0.1
|
|
29753
|
-
--voxel-external-fill [size] Fill exterior voxels by dilation from seed. Default size: 1.6
|
|
29754
|
-
--voxel-floor-fill [size] Fill below-floor voxels by upward column walk from bottom. Default size: 1.6
|
|
29755
|
-
--voxel-carve [h,r] Carve navigable space using capsule flood fill from seed. Default: 1.6,0.2
|
|
29756
|
-
--seed-pos <x,y,z> Seed position for voxel processing and --filter-cluster. Default: 0,0,0
|
|
29757
|
-
-K, --collision-mesh [smooth|faces] Generate collision mesh (.collision.glb). Default shape: smooth
|
|
29758
|
-
|
|
29759
|
-
EXAMPLES
|
|
29760
|
-
# Scale then translate
|
|
29761
|
-
splat-transform bunny.ply -s 0.5 -t 0,0,10 bunny-scaled.ply
|
|
29762
|
-
|
|
29763
|
-
# Merge two files with transforms and compress to SOG format
|
|
29764
|
-
splat-transform -w cloudA.ply -r 0,90,0 cloudB.ply -s 2 merged.sog
|
|
29765
29789
|
|
|
29766
|
-
|
|
29767
|
-
|
|
29768
|
-
|
|
29769
|
-
|
|
29770
|
-
splat-transform gen-grid.mjs -p width=500,height=500,scale=0.1 grid.ply
|
|
29771
|
-
|
|
29772
|
-
# Generate LOD with custom chunk size and node split size
|
|
29773
|
-
splat-transform -O 0,1,2 -C 1024 -X 32 input.lcc output/lod-meta.json
|
|
29790
|
+
GPU (used by SOG compression and GPU voxelization: --filter-cluster, --filter-floaters, .voxel.json output)
|
|
29791
|
+
-L, --list-gpus List available GPU adapters and exit
|
|
29792
|
+
-g, --gpu <n|cpu> Device for GPU operations: GPU adapter index | 'cpu'
|
|
29793
|
+
('cpu' disables GPU and is incompatible with GPU-only features)
|
|
29774
29794
|
|
|
29775
|
-
|
|
29776
|
-
|
|
29795
|
+
SOG COMPRESSION (.sog, meta.json, lod-meta.json, .html outputs)
|
|
29796
|
+
-i, --iterations <n> SH compression iterations (more=better). Default: 10
|
|
29777
29797
|
|
|
29778
|
-
|
|
29779
|
-
|
|
29798
|
+
HTML VIEWER OUTPUT (.html)
|
|
29799
|
+
-E, --viewer-settings <settings.json> HTML viewer settings JSON file
|
|
29800
|
+
-U, --unbundled Generate unbundled HTML viewer with separate files
|
|
29780
29801
|
|
|
29781
|
-
|
|
29782
|
-
|
|
29802
|
+
LCC INPUT (.lcc)
|
|
29803
|
+
-O, --lod-select <n,n,...> Comma-separated LOD levels to read
|
|
29783
29804
|
|
|
29784
|
-
|
|
29785
|
-
|
|
29805
|
+
LOD OUTPUT (lod-meta.json)
|
|
29806
|
+
-C, --lod-chunk-count <n> Approximate Gaussians per LOD chunk in K. Default: 512
|
|
29807
|
+
-X, --lod-chunk-extent <n> Approximate LOD chunk size in world units. Default: 16
|
|
29786
29808
|
|
|
29787
|
-
|
|
29788
|
-
|
|
29809
|
+
VOXEL OUTPUT (.voxel.json)
|
|
29810
|
+
--voxel-params [size,opacity] Voxel size + opacity threshold. Default: 0.05,0.1
|
|
29811
|
+
--voxel-external-fill [size] Seal exterior voxels via boundary flood fill (interior scenes). Default: 1.6
|
|
29812
|
+
--voxel-floor-fill [radius] Fill columns upward from bottom (exterior scenes). Default: 1.6
|
|
29813
|
+
--voxel-carve [h,r] Carve navigable space via capsule flood fill from seed. Default: 1.6,0.2
|
|
29814
|
+
--seed-pos <x,y,z> Seed position for voxel fill/carve and --filter-cluster. Default: 0,0,0
|
|
29815
|
+
-K, --collision-mesh [smooth|faces] Generate collision mesh (.collision.glb). Default: smooth
|
|
29789
29816
|
|
|
29790
|
-
|
|
29791
|
-
|
|
29817
|
+
EXAMPLES
|
|
29818
|
+
# Convert formats
|
|
29819
|
+
splat-transform input.ply output.sog
|
|
29792
29820
|
|
|
29793
|
-
#
|
|
29794
|
-
splat-transform
|
|
29821
|
+
# Merge files with transforms
|
|
29822
|
+
splat-transform -w a.ply -r 0,90,0 b.ply -s 2 merged.sog
|
|
29795
29823
|
|
|
29796
|
-
#
|
|
29797
|
-
splat-transform
|
|
29824
|
+
# Generate voxel collision data
|
|
29825
|
+
splat-transform input.ply --filter-cluster output.voxel.json
|
|
29798
29826
|
|
|
29799
|
-
|
|
29800
|
-
splat-transform https://example.com/scene.ply scene.sog
|
|
29827
|
+
More examples: https://github.com/playcanvas/splat-transform#examples
|
|
29801
29828
|
`;
|
|
29802
29829
|
const main = async () => {
|
|
29803
29830
|
const startTime = performance.now();
|