@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/dist/index.mjs
CHANGED
|
@@ -6275,11 +6275,11 @@ class CompressedChunk {
|
|
|
6275
6275
|
/**
|
|
6276
6276
|
* The splat-transform version (semver MAJOR.MINOR.PATCH).
|
|
6277
6277
|
*/
|
|
6278
|
-
const version = '2.0.
|
|
6278
|
+
const version = '2.0.6';
|
|
6279
6279
|
/**
|
|
6280
6280
|
* The splat-transform revision (short Git hash of HEAD at build time).
|
|
6281
6281
|
*/
|
|
6282
|
-
const revision = '
|
|
6282
|
+
const revision = 'aa3dbae';
|
|
6283
6283
|
|
|
6284
6284
|
const generatedByString = `Generated by splat-transform ${version}`;
|
|
6285
6285
|
const chunkProps = [
|
|
@@ -8267,13 +8267,17 @@ class GpuDilation {
|
|
|
8267
8267
|
// Extract: clear bufferA, dispatch extract from sparse src into bufferA.
|
|
8268
8268
|
this.dispatchClear(slot, slot.bufferA, numWords);
|
|
8269
8269
|
this.dispatchExtract(slot, minBx, minBy, minBz, outerBx, outerBy, outerBz, numXWords);
|
|
8270
|
-
//
|
|
8271
|
-
//
|
|
8272
|
-
//
|
|
8273
|
-
//
|
|
8274
|
-
// writes
|
|
8275
|
-
//
|
|
8276
|
-
|
|
8270
|
+
// Force a queue submission boundary between the extract pass (which
|
|
8271
|
+
// writes bufferA via atomicOr — bound as `storage, read_write` with
|
|
8272
|
+
// `array<atomic<u32>>`) and the dilate-X pass (which reads bufferA
|
|
8273
|
+
// bound as `storage, read` with `array<u32>`). Without this, the
|
|
8274
|
+
// atomic writes are not reliably visible to the next pass — dilation
|
|
8275
|
+
// produces empty output. Other inter-pass transitions in this
|
|
8276
|
+
// pipeline (clear→extract, dilateX→Z→Y, Y→compact) rely on automatic
|
|
8277
|
+
// intra-encoder synchronization without issue. Verified raw Dawn
|
|
8278
|
+
// synchronizes this case correctly, so the bug is somewhere in
|
|
8279
|
+
// PlayCanvas's compute dispatch / bind-group path.
|
|
8280
|
+
this.device.submit();
|
|
8277
8281
|
// X-pass: A → B
|
|
8278
8282
|
this.dispatchX(slot, slot.bufferA, slot.bufferB, numXWords, outerNy, outerNz, halfExtentXZ);
|
|
8279
8283
|
// Z-pass: B → A
|
|
@@ -9688,8 +9692,16 @@ const binIndices = (parent, lod) => {
|
|
|
9688
9692
|
*/
|
|
9689
9693
|
const writeLod = async (options, fs) => {
|
|
9690
9694
|
const { filename, iterations, createDevice, chunkCount, chunkExtent } = options;
|
|
9691
|
-
|
|
9692
|
-
|
|
9695
|
+
// Operate in PLY space so per-leaf bounds in tree.bound are in the same
|
|
9696
|
+
// coordinate frame as the SOG chunk data emitted by writeSog (which also
|
|
9697
|
+
// converts to Transform.PLY). Without this, view-dependent streaming
|
|
9698
|
+
// built on tree.bound picks the wrong chunks because the bounds are
|
|
9699
|
+
// 180°-Z-rotated relative to the splat positions inside them.
|
|
9700
|
+
// This intentionally mutates the input tables to avoid doubling peak
|
|
9701
|
+
// memory during LOD export. Callers should treat writeLod as consuming its
|
|
9702
|
+
// DataTable inputs.
|
|
9703
|
+
const dataTable = convertToSpace(options.dataTable, Transform.PLY, true);
|
|
9704
|
+
const envDataTable = options.envDataTable ? convertToSpace(options.envDataTable, Transform.PLY, true) : null;
|
|
9693
9705
|
const outputDir = dirname(filename);
|
|
9694
9706
|
// ensure top-level output folder exists
|
|
9695
9707
|
await fs.mkdir(outputDir);
|
|
@@ -15201,8 +15213,14 @@ const voxelizeToBuffer = async (bvh, gpuVoxelization, gridBounds, voxelResolutio
|
|
|
15201
15213
|
const buffer = new BlockMaskBuffer();
|
|
15202
15214
|
const bStride = numBlocksX * numBlocksY;
|
|
15203
15215
|
const batchSize = 16;
|
|
15204
|
-
|
|
15205
|
-
|
|
15216
|
+
// Caps for one mega-flush. Sized to keep a single compute dispatch's
|
|
15217
|
+
// wall-clock comfortably under Windows D3D12 TDR (~2s watchdog) on slower
|
|
15218
|
+
// GPUs without materially affecting throughput on fast GPUs (the
|
|
15219
|
+
// double-buffered slots keep the GPU fed across the extra flushes).
|
|
15220
|
+
// Output is bit-identical regardless: each batch's full Gaussian list rides
|
|
15221
|
+
// with it, and slot buffers grow on demand to accommodate any oversize batch.
|
|
15222
|
+
const MEGA_MAX_BATCHES = 256;
|
|
15223
|
+
const MEGA_MAX_INDICES = 2 * 1024 * 1024;
|
|
15206
15224
|
const maxBlocks = GpuVoxelization.MAX_BLOCKS_PER_BATCH;
|
|
15207
15225
|
const numSlots = GpuVoxelization.NUM_SLOTS;
|
|
15208
15226
|
const slotIndexArrays = [];
|
|
@@ -17008,21 +17026,32 @@ const cropToNavigable = (grid, gridBounds, voxelResolution) => {
|
|
|
17008
17026
|
return { grid, gridBounds };
|
|
17009
17027
|
}
|
|
17010
17028
|
const { minBx, minBy, minBz, maxBx, maxBy, maxBz } = navBounds;
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17029
|
+
// Pad by 1 block on each side so the cropped grid retains the solid wall
|
|
17030
|
+
// blocks immediately surrounding the navigable cavity. Matches the
|
|
17031
|
+
// MARGIN = 1 pattern used by carve() before this re-crop strips it. The
|
|
17032
|
+
// collision-mesh extractors treat out-of-grid as empty, so without this
|
|
17033
|
+
// padding the mesh has holes wherever the cavity reaches the cropped
|
|
17034
|
+
// boundary; with it, the mesh extractor sees a real SOLID→EMPTY
|
|
17035
|
+
// transition at the cavity edge and emits a sealed wall there.
|
|
17036
|
+
const MARGIN = 1;
|
|
17037
|
+
const cropMinBx = Math.max(0, minBx - MARGIN);
|
|
17038
|
+
const cropMinBy = Math.max(0, minBy - MARGIN);
|
|
17039
|
+
const cropMinBz = Math.max(0, minBz - MARGIN);
|
|
17040
|
+
const cropMaxBx = Math.min(nbx, maxBx + 1 + MARGIN);
|
|
17041
|
+
const cropMaxBy = Math.min(nby, maxBy + 1 + MARGIN);
|
|
17042
|
+
const cropMaxBz = Math.min(nbz, maxBz + 1 + MARGIN);
|
|
17043
|
+
if (cropMinBx === 0 && cropMinBy === 0 && cropMinBz === 0 &&
|
|
17015
17044
|
cropMaxBx === nbx && cropMaxBy === nby && cropMaxBz === nbz) {
|
|
17016
17045
|
return { grid, gridBounds };
|
|
17017
17046
|
}
|
|
17018
17047
|
const cropBar = logger.bar('Cropping grid', grid.types.length);
|
|
17019
|
-
const croppedGrid = grid.cropTo(
|
|
17048
|
+
const croppedGrid = grid.cropTo(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz, done => cropBar.update(done));
|
|
17020
17049
|
cropBar.end();
|
|
17021
17050
|
const blockSize = 4 * voxelResolution;
|
|
17022
|
-
const croppedMin = new Vec3(gridBounds.min.x +
|
|
17051
|
+
const croppedMin = new Vec3(gridBounds.min.x + cropMinBx * blockSize, gridBounds.min.y + cropMinBy * blockSize, gridBounds.min.z + cropMinBz * blockSize);
|
|
17023
17052
|
const croppedBounds = {
|
|
17024
17053
|
min: croppedMin,
|
|
17025
|
-
max: new Vec3(croppedMin.x + (cropMaxBx -
|
|
17054
|
+
max: new Vec3(croppedMin.x + (cropMaxBx - cropMinBx) * blockSize, croppedMin.y + (cropMaxBy - cropMinBy) * blockSize, croppedMin.z + (cropMaxBz - cropMinBz) * blockSize)
|
|
17026
17055
|
};
|
|
17027
17056
|
return { grid: croppedGrid, gridBounds: croppedBounds };
|
|
17028
17057
|
};
|
|
@@ -17149,13 +17178,27 @@ const writeVoxel = async (options, fs) => {
|
|
|
17149
17178
|
gpuVoxelization = new GpuVoxelization(device);
|
|
17150
17179
|
gpuVoxelization.uploadAllGaussians(pcDataTable, extentsResult.extents);
|
|
17151
17180
|
// Align grid bounds to block boundaries BEFORE voxelization so the
|
|
17152
|
-
// block coordinates used during voxelization match what the reader
|
|
17153
|
-
//
|
|
17154
|
-
//
|
|
17181
|
+
// block coordinates used during voxelization match what the reader
|
|
17182
|
+
// expects. fillExterior and fillFloor both need a margin of empty
|
|
17183
|
+
// voxels outside the splat's tight 3-sigma extents to do their job:
|
|
17184
|
+
// fillExterior so the boundary-face flood seeds survive its dilation
|
|
17185
|
+
// (notably below the floor), fillFloor so its column walk has empty
|
|
17186
|
+
// XZ columns to convert into wall pillars and the dilation halo to
|
|
17187
|
+
// extend the floor footprint outward.
|
|
17188
|
+
//
|
|
17189
|
+
// Lateral pad combines both as `dilation_radius + 1` voxels per side.
|
|
17190
|
+
// Vertical pad is only contributed by exteriorPad — fillFloor's
|
|
17191
|
+
// dilation is XZ-only, and Y padding would extend the wall pillars
|
|
17192
|
+
// above the splat's natural ceiling and below its floor.
|
|
17155
17193
|
const exteriorPad = hasFillExterior ?
|
|
17156
17194
|
(Math.ceil(navExteriorRadius / voxelResolution) + 1) * voxelResolution :
|
|
17157
17195
|
0;
|
|
17158
|
-
|
|
17196
|
+
const floorPad = hasFloorFill ?
|
|
17197
|
+
(Math.ceil(floorFillDilation / voxelResolution) + 1) * voxelResolution :
|
|
17198
|
+
0;
|
|
17199
|
+
const padXZ = Math.max(exteriorPad, floorPad);
|
|
17200
|
+
const padY = exteriorPad;
|
|
17201
|
+
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);
|
|
17159
17202
|
const buffer = await voxelizeToBuffer(bvh, gpuVoxelization, gridBounds, voxelResolution, opacityCutoff);
|
|
17160
17203
|
bvh = null;
|
|
17161
17204
|
pcDataTable = null;
|