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