@voluma/vlam 0.3.2 → 0.4.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 (40) hide show
  1. package/README.md +2 -2
  2. package/dist/core/compute-sorter.d.ts +7 -1
  3. package/dist/core/index.d.ts +2 -2
  4. package/dist/core/radix-sorter.d.ts +6 -0
  5. package/dist/core/sort-worker-protocol.d.ts +2 -0
  6. package/dist/core/sorter.d.ts +4 -4
  7. package/dist/core/splat-mesh-types.d.ts +22 -5
  8. package/dist/core/splat-mesh.d.ts +11 -2
  9. package/dist/core/splat-sort-bounds.d.ts +17 -0
  10. package/dist/core/worker-sorter.d.ts +7 -5
  11. package/dist/formats/lcc.js +276 -277
  12. package/dist/formats/lcc.js.map +1 -1
  13. package/dist/formats/rad/index.d.ts +1 -1
  14. package/dist/formats/rad/parse-rad.d.ts +1 -1
  15. package/dist/formats/rad.js.map +1 -1
  16. package/dist/index.js +3 -3
  17. package/dist/lod-scheduler-ChoZyEeq.js +466 -0
  18. package/dist/lod-scheduler-ChoZyEeq.js.map +1 -0
  19. package/dist/radix-sorter-tSffmerk.js +181 -0
  20. package/dist/radix-sorter-tSffmerk.js.map +1 -0
  21. package/dist/relighting-qaANKC4Z.js +997 -0
  22. package/dist/relighting-qaANKC4Z.js.map +1 -0
  23. package/dist/{splat-mesh-CsLOQb08.js → splat-mesh-B1KlWHvx.js} +545 -513
  24. package/dist/splat-mesh-B1KlWHvx.js.map +1 -0
  25. package/dist/splat-mesh-types-BZIko-_9.js.map +1 -1
  26. package/dist/static-lod.js +1 -1
  27. package/dist/streaming/lod-scheduler.d.ts +32 -0
  28. package/dist/streaming/lod-source.d.ts +15 -9
  29. package/dist/streaming/streamed-splat-mesh.d.ts +38 -22
  30. package/dist/streaming.js +959 -927
  31. package/dist/streaming.js.map +1 -1
  32. package/dist/unified.js +17 -17
  33. package/package.json +1 -1
  34. package/dist/lod-scheduler-B0a_uBlv.js +0 -383
  35. package/dist/lod-scheduler-B0a_uBlv.js.map +0 -1
  36. package/dist/radix-sorter-BrbUg_CV.js +0 -178
  37. package/dist/radix-sorter-BrbUg_CV.js.map +0 -1
  38. package/dist/relighting-Tiwep8yd.js +0 -977
  39. package/dist/relighting-Tiwep8yd.js.map +0 -1
  40. package/dist/splat-mesh-CsLOQb08.js.map +0 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ renderer.setAnimationLoop(() => {
51
51
  ```
52
52
 
53
53
 
54
- Supported formats: `.sog`, `.ply`, `.spz`, `.splat`, `.ksplat`, `.lcc`, `.lcc2`, `.rad`, `.radc`.
54
+ Supported formats: `.sog`, `.ply`, `.spz`, `.splat`, `.ksplat`, `.lcc`, `.lcc2`, `.rad`.
55
55
 
56
56
 
57
57
  ## Contributing
@@ -62,4 +62,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/architecture.md](docs/architect
62
62
 
63
63
  [MIT](LICENSE). Third-party notices: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
64
64
 
65
- Made in the EU by [Voluma](https://voluma.ai).
65
+ Made by [Voluma](https://voluma.ai).
@@ -1,5 +1,6 @@
1
1
  import * as THREE from 'three/webgpu';
2
2
  import type { SplatSorter } from './sorter';
3
+ import type { SplatSortMetric } from './splat-mesh-types';
3
4
  import type { uniformArray } from 'three/tsl';
4
5
  /**
5
6
  * Optional per-source world transform for a unified {@link MergedSplatMesh} pool.
@@ -78,7 +79,9 @@ export declare class ComputeSorter implements SplatSorter {
78
79
  private readonly mirrors;
79
80
  /** Set by {@link dispose}; makes a second dispose a no-op. */
80
81
  private disposed;
81
- /** Row 2 of the model-view matrix; view-space z = row2 · (position, 1). */
82
+ /** Rows of the model-view matrix used by the selected sort metric. */
83
+ private readonly viewRow0;
84
+ private readonly viewRow1;
82
85
  private readonly viewRow2;
83
86
  private readonly depthMin;
84
87
  private readonly depthScale;
@@ -87,6 +90,7 @@ export declare class ComputeSorter implements SplatSorter {
87
90
  /** Highest bucket index this sort uses; see {@link effectiveBucketCount}. */
88
91
  private readonly bucketMax;
89
92
  private readonly viewCenter;
93
+ private readonly sortMetric;
90
94
  constructor(options: {
91
95
  renderer: THREE.WebGPURenderer;
92
96
  /** Pool capacity in splats; all buffers are sized once from this. */
@@ -103,6 +107,8 @@ export declare class ComputeSorter implements SplatSorter {
103
107
  sourceIndexAttribute: THREE.StorageBufferAttribute;
104
108
  /** Per-source world transform for a unified pool; omit for a single mesh. */
105
109
  perSource?: PerSourceSortTransform;
110
+ /** Camera-space ordering key. Default `'depth'`. */
111
+ sortMetric?: SplatSortMetric;
106
112
  });
107
113
  /**
108
114
  * Buckets to actually use for `activeCount` splats, rounded up to a power of
@@ -14,7 +14,7 @@
14
14
  * - `@voluma/vlam/effects` - tree-shakeable modifier presets
15
15
  * - `@voluma/vlam/formats/ply` - 3DGS `.ply`, raw and compressed
16
16
  * - `@voluma/vlam/formats/sog` - PlayCanvas SOG, bundled and unbundled
17
- * - `@voluma/vlam/formats/rad` - Spark `.rad`/`.radc`
17
+ * - `@voluma/vlam/formats/rad` - Spark `.rad`
18
18
  * - `@voluma/vlam/formats/lcc` - XGRIDS LCC / `.lcc2` + collision helpers
19
19
  * - `@voluma/vlam/formats/spz` - Niantic `.spz`
20
20
  * - `@voluma/vlam/formats/splat` - antimatter15 `.splat`
@@ -25,7 +25,7 @@
25
25
  *
26
26
  * @module core
27
27
  */
28
- export { SplatMesh, MAX_SH_BANDS, DEFAULT_FOVEATION_TARGET_PX, DEFAULT_FOVEATION_DRAW_BUDGET, resolveSplatPerformanceProfile, type UnifiedSourceView, type SplatMeshOptions, type SplatUpdateOptions, type SplatSortStrategy, type SplatPerformanceProfile, type SplatFoveationMode, type SplatRange, type SplatPickOptions, type SplatPickResult, type SplatNearestResult, type SplatRayResult, type SplatHeightResult, type SplatChannelType, type SplatChannelOptions, } from './splat-mesh';
28
+ export { SplatMesh, MAX_SH_BANDS, DEFAULT_FOVEATION_TARGET_PX, DEFAULT_FOVEATION_DRAW_BUDGET, resolveSplatPerformanceProfile, type UnifiedSourceView, type SplatMeshOptions, type SplatUpdateOptions, type SplatSortStrategy, type SplatSortMetric, type SplatPerformanceProfile, type SplatFoveationMode, type SplatRange, type SplatPickOptions, type SplatPickResult, type SplatNearestResult, type SplatRayResult, type SplatHeightResult, type SplatChannelType, type SplatChannelOptions, } from './splat-mesh';
29
29
  export { SplatPool, type SplatPoolOptions } from './splat-mesh-pool';
30
30
  export { SPLAT_DATA_TEXTURE_WIDTH, deviceMaxTextureSize, assertPoolRowsFitDevice, } from './splat-mesh-pool';
31
31
  export { MAX_DOF_RADIUS_PX, MAX_DOF_VARIANCE, apertureAngleFromSize, computeDofCocVariancePx2, computeDofOpacityFade, clampDepthOfFieldSettings, type DepthOfFieldSettings, } from './depth-of-field';
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import * as THREE from 'three/webgpu';
6
6
  import type { SplatSorter } from './sorter';
7
+ import type { SplatSortMetric } from './splat-mesh-types';
7
8
  /**
8
9
  * Stable four-bit-per-pass WebGPU radix sorter.
9
10
  *
@@ -22,6 +23,8 @@ export declare class RadixSorter implements SplatSorter {
22
23
  private readonly mirrors;
23
24
  /** Set by {@link dispose}; makes a second dispose a no-op. */
24
25
  private disposed;
26
+ private readonly viewRow0;
27
+ private readonly viewRow1;
25
28
  private readonly viewRow2;
26
29
  private readonly depthMin;
27
30
  private readonly depthScale;
@@ -29,6 +32,7 @@ export declare class RadixSorter implements SplatSorter {
29
32
  private readonly viewCenter;
30
33
  /** Exact mode avoids scene-bounds quantization entirely. */
31
34
  private readonly exactDepth;
35
+ private readonly sortMetric;
32
36
  constructor(options: {
33
37
  renderer: THREE.WebGPURenderer;
34
38
  capacity: number;
@@ -40,6 +44,8 @@ export declare class RadixSorter implements SplatSorter {
40
44
  sourceIndexAttribute: THREE.StorageBufferAttribute;
41
45
  /** Keep every Float32 depth bit instead of quantizing to 24 bits. */
42
46
  exactDepth?: boolean;
47
+ /** Camera-space ordering key. Default `'depth'`. */
48
+ sortMetric?: SplatSortMetric;
43
49
  });
44
50
  sort(modelView: THREE.Matrix4, activeCount: number, bounds: THREE.Sphere): boolean;
45
51
  dispose(): void;
@@ -21,6 +21,8 @@ export interface WriteMessage {
21
21
  }
22
22
  export interface SortMessage {
23
23
  type: 'sort';
24
+ /** Camera-space ordering key. */
25
+ sortMetric: 'depth' | 'radial';
24
26
  /** Column-major 4×4 model-view matrix elements. */
25
27
  modelView: Float32Array;
26
28
  /** Active ranges as (start, count) pairs of pool splat indices, in
@@ -5,10 +5,10 @@ import type * as THREE from 'three/webgpu';
5
5
  *
6
6
  * - `ComputeSorter`: GPU counting sort in TSL compute (WebGPU backend);
7
7
  * supports dynamic-capacity meshes via source-index indirection.
8
- * - `WorkerSorter`: CPU counting sort in a Web Worker (WebGL2 fallback,
9
- * which has no compute shaders); handles static and dynamic-capacity
10
- * (streamed) meshes alike by mirroring the pool's centers and sorting
11
- * the active spans.
8
+ * - `WorkerSorter`: stable CPU radix sort in a Web Worker (the WebGL2
9
+ * fallback and an explicit WebGPU stability option); handles static and
10
+ * dynamic-capacity meshes alike by mirroring the pool's centers and
11
+ * sorting the active spans.
12
12
  * - `RadixSorter`: experimental GPU radix (opt-in via `sortStrategy: 'radix'`).
13
13
  */
14
14
  export type SplatSorterKind = 'counting' | 'radix' | 'worker';
@@ -66,18 +66,33 @@ export interface SplatMeshOptions {
66
66
  /**
67
67
  * Minimum interval between WebGPU sorts while the camera moves. When
68
68
  * omitted, the interval adapts to the active splat count. Use `0` to sort
69
- * every changed frame. WebGL worker sorting is unaffected.
69
+ * every changed frame. WebGL worker sorting is unaffected; a worker selected
70
+ * explicitly on WebGPU still observes this submission cadence.
70
71
  */
71
72
  sortIntervalMs?: number;
72
73
  /**
73
74
  * WebGPU sorter used for A/B validation. Defaults to the proven counting
74
- * sorter. `'radix'` keeps the fast 24-bit key path; `'exact'` lazy-loads a
75
- * stable 32-bit Float32-depth radix path that avoids scene-range
76
- * quantization. The first frames may skip sorting until the module resolves.
75
+ * sorter. `'worker'` keeps rendering on WebGPU but sorts asynchronously in
76
+ * the stable CPU worker also used by the WebGL fallback; this is useful for
77
+ * Spark-like temporal stability on captures with extreme position outliers.
78
+ * `'radix'` keeps the fast 24-bit GPU key path; `'exact'` lazy-loads a 32-bit
79
+ * Float32-depth GPU radix path. The first frames may skip GPU radix sorting
80
+ * until the module resolves.
77
81
  *
78
82
  * @experimental Radix strategies may change in a minor release.
79
83
  */
80
84
  sortStrategy?: SplatSortStrategy;
85
+ /**
86
+ * Geometric key used to order transparent splats back-to-front.
87
+ *
88
+ * `'depth'` (default) sorts along the camera's view axis and gives the most
89
+ * accurate alpha order for a fixed view. `'radial'` sorts by distance from
90
+ * the camera, matching Spark's stable default: camera rotation alone leaves
91
+ * the order unchanged, which greatly reduces whole-scene shimmer while
92
+ * orbiting dense captures. Pair it with `sortStrategy: 'exact'` when extreme
93
+ * position outliers would make quantized buckets too coarse.
94
+ */
95
+ sortMetric?: SplatSortMetric;
81
96
  /**
82
97
  * Render-quality policy. `smooth` rejects negligible projected contributions.
83
98
  *
@@ -272,7 +287,9 @@ export interface SplatMeshOptions {
272
287
  maxTextureSize?: number;
273
288
  }
274
289
  /** Available WebGPU depth-sort implementations. */
275
- export type SplatSortStrategy = 'counting' | 'radix' | 'exact';
290
+ export type SplatSortStrategy = 'counting' | 'worker' | 'radix' | 'exact';
291
+ /** Camera-space key used for back-to-front splat ordering. */
292
+ export type SplatSortMetric = 'depth' | 'radial';
276
293
  /** Controls optional work during a per-frame source update. */
277
294
  export interface SplatUpdateOptions {
278
295
  /** Leave sorting to {@link UnifiedSplatMesh}; uploads and LOD state still update. */
@@ -194,8 +194,10 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
194
194
  * storage-attribute GPU buffers that never sat in a geometry. */
195
195
  private lastRenderer;
196
196
  private readonly currentModelView;
197
+ /** Pose components that can actually change the selected sort key. */
198
+ private readonly currentSortState;
197
199
  /** Initialized to an impossible matrix so the first frame always sorts. */
198
- private readonly lastSortedModelView;
200
+ private readonly lastSortedState;
199
201
  private readonly sortScheduler;
200
202
  /** One-frame queue-headroom hint used before a staged atomic commit. */
201
203
  private deferSortRequestOnce;
@@ -204,6 +206,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
204
206
  /** The active list the current depth order was built from. */
205
207
  private sortedActiveListVersion;
206
208
  private readonly sortStrategy;
209
+ private readonly sortMetric;
207
210
  /** Resolved only when `sortStrategy === 'radix'`; see {@link ensureRadixSorter}. */
208
211
  private RadixSorterCtor;
209
212
  private radixSorterLoad;
@@ -604,7 +607,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
604
607
  renderView(camera: THREE.PerspectiveCamera, renderer: THREE.WebGPURenderer, target?: THREE.RenderTarget | null): void;
605
608
  /**
606
609
  * Sorts the shared order buffer for a secondary view's camera, bypassing the
607
- * primary view's sort scheduler and its `lastSortedModelView` record (that
610
+ * primary view's sort scheduler and its last-sorted pose record (that
608
611
  * state belongs to `update()`'s camera). WebGPU dispatches synchronously into
609
612
  * the render queue, so the following draw reads this order.
610
613
  */
@@ -722,6 +725,12 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
722
725
  * order then changes without any camera motion.
723
726
  */
724
727
  protected invalidateSort(): void;
728
+ /**
729
+ * Writes the camera/mesh state that can change the selected ordering key.
730
+ * Radial distance is invariant under camera rotation, so its state contains
731
+ * the mesh's world linear transform and camera-relative translation only.
732
+ */
733
+ private writeSortState;
725
734
  private createSorter;
726
735
  /** Prefetches the experimental radix sorter; safe to call repeatedly. */
727
736
  private ensureRadixSorter;
@@ -9,3 +9,20 @@ import type * as THREE from 'three/webgpu';
9
9
  * compose into a shear.
10
10
  */
11
11
  export declare function viewDepthRadius(modelView: THREE.Matrix4, radius: number): number;
12
+ /**
13
+ * A conservative camera-space radius for radial-distance quantization.
14
+ *
15
+ * Uses `sqrt(||A^T A||∞)`, an upper bound on the linear part's largest
16
+ * singular value. Unlike `Matrix4.getMaxScaleOnAxis()`, it cannot under-bound
17
+ * a sphere when a transformed hierarchy introduces shear.
18
+ */
19
+ export declare function viewRadialRadius(modelView: THREE.Matrix4, radius: number): number;
20
+ /**
21
+ * Builds the pose signature relevant to radial sorting.
22
+ *
23
+ * World-space distance depends on the mesh's linear transform and its
24
+ * translation relative to the camera, but not on camera orientation. The
25
+ * scheduler compares this matrix so rotating in place does not dispatch an
26
+ * identical radial sort.
27
+ */
28
+ export declare function radialSortState(meshWorld: THREE.Matrix4, cameraWorld: THREE.Matrix4, out: THREE.Matrix4): THREE.Matrix4;
@@ -1,10 +1,11 @@
1
1
  import type * as THREE from 'three/webgpu';
2
2
  import type { SplatSorter } from './sorter';
3
+ import type { SplatSortMetric } from './splat-mesh-types';
3
4
  /**
4
- * CPU depth sorter: counting sort in a Web Worker. Used on the WebGL2
5
- * fallback backend, which has no compute shaders. Works for static and
6
- * dynamic-capacity (streamed) meshes alike: the worker keeps a mirror of
7
- * the pool's centers, and each sort covers only the active pool spans.
5
+ * Stable CPU radix sorter in a Web Worker. Used by the WebGL2 fallback and,
6
+ * when explicitly selected, alongside WebGPU rendering for Spark-like sort
7
+ * cadence. Works for static and dynamic-capacity meshes alike: the worker
8
+ * keeps a mirror of the pool's centers and sorts only the active spans.
8
9
  *
9
10
  * One sort runs at a time; requests that arrive while the worker is busy
10
11
  * are declined so the caller retries with the then-current camera on a
@@ -15,6 +16,7 @@ export declare class WorkerSorter implements SplatSorter {
15
16
  private readonly worker;
16
17
  private readonly splatIndexAttribute;
17
18
  private readonly host;
19
+ private readonly sortMetric;
18
20
  private inFlight;
19
21
  /** Set by {@link dispose}; drops any already-delivered order message. */
20
22
  private disposed;
@@ -25,7 +27,7 @@ export declare class WorkerSorter implements SplatSorter {
25
27
  private lastSubmittedAt;
26
28
  private lastCompletedAt;
27
29
  private lastLatencyMs;
28
- constructor(host: WorkerSorterHost);
30
+ constructor(host: WorkerSorterHost, sortMetric?: SplatSortMetric);
29
31
  sort(modelView: THREE.Matrix4, _activeCount: number, _bounds: THREE.Sphere): boolean;
30
32
  /** Internal timing diagnostics used by the demo's opt-in XR A/B harness. */
31
33
  snapshot(): {