@voluma/vlam 0.1.0 → 0.2.1

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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -0
  3. package/THIRD_PARTY_NOTICES.md +86 -0
  4. package/dist/budget-governor.d.ts +154 -0
  5. package/dist/camera-budget-governor.d.ts +234 -0
  6. package/dist/chunk-cache-budget.d.ts +156 -0
  7. package/dist/chunk-fetch-scheduler.d.ts +168 -0
  8. package/dist/chunk-loader.d.ts +85 -0
  9. package/dist/compute-sorter.d.ts +131 -0
  10. package/dist/create-splat-renderer.d.ts +99 -0
  11. package/dist/dataset-source.d.ts +55 -0
  12. package/dist/depth-of-field.d.ts +73 -0
  13. package/dist/effects.d.ts +337 -0
  14. package/dist/effects.js +180 -0
  15. package/dist/effects.js.map +1 -0
  16. package/dist/formats/ksplat/index.d.ts +8 -0
  17. package/dist/formats/ksplat/parse-ksplat.d.ts +9 -0
  18. package/dist/formats/ksplat.js +167 -0
  19. package/dist/formats/ksplat.js.map +1 -0
  20. package/dist/formats/lcc/collision-mesh.d.ts +37 -0
  21. package/dist/formats/lcc/collision-partition.d.ts +35 -0
  22. package/dist/formats/lcc/index.d.ts +16 -0
  23. package/dist/formats/lcc/lcc.d.ts +28 -0
  24. package/dist/formats/lcc/lcc2-transform.d.ts +18 -0
  25. package/dist/formats/lcc/lcc2.d.ts +4 -0
  26. package/dist/formats/lcc/parse-collision-lci.d.ts +28 -0
  27. package/dist/formats/lcc/parse-lcc.d.ts +177 -0
  28. package/dist/formats/lcc/parse-mesh-ply.d.ts +21 -0
  29. package/dist/formats/lcc.js +969 -0
  30. package/dist/formats/lcc.js.map +1 -0
  31. package/dist/formats/ply/index.d.ts +12 -0
  32. package/dist/formats/ply/parse-compressed-ply.d.ts +16 -0
  33. package/dist/formats/ply/parse-splat-ply.d.ts +46 -0
  34. package/dist/formats/ply.js +230 -0
  35. package/dist/formats/ply.js.map +1 -0
  36. package/dist/formats/rad/frontier-worker-protocol.d.ts +177 -0
  37. package/dist/formats/rad/index.d.ts +10 -0
  38. package/dist/formats/rad/parse-rad.d.ts +88 -0
  39. package/dist/formats/rad/rad-column-decoders.d.ts +15 -0
  40. package/dist/formats/rad/rad-foveated-source.d.ts +71 -0
  41. package/dist/formats/rad/rad-parent-sizes.d.ts +45 -0
  42. package/dist/formats/rad/rad.d.ts +132 -0
  43. package/dist/formats/rad.js +1098 -0
  44. package/dist/formats/rad.js.map +1 -0
  45. package/dist/formats/sog/index.d.ts +9 -0
  46. package/dist/formats/sog/parse-sog.d.ts +45 -0
  47. package/dist/formats/sog.js +257 -0
  48. package/dist/formats/sog.js.map +1 -0
  49. package/dist/formats/splat/index.d.ts +8 -0
  50. package/dist/formats/splat/parse-splat.d.ts +10 -0
  51. package/dist/formats/splat.js +39 -0
  52. package/dist/formats/splat.js.map +1 -0
  53. package/dist/formats/spz/index.d.ts +8 -0
  54. package/dist/formats/spz/parse-spz.d.ts +10 -0
  55. package/dist/formats/spz.js +218 -0
  56. package/dist/formats/spz.js.map +1 -0
  57. package/dist/frontier-worker-CAnK_Cmu.js +566 -0
  58. package/dist/frontier-worker-CAnK_Cmu.js.map +1 -0
  59. package/dist/half-float-Cs4FDq8Q.js +30 -0
  60. package/dist/half-float-Cs4FDq8Q.js.map +1 -0
  61. package/dist/half-float.d.ts +15 -0
  62. package/dist/index.d.ts +54 -0
  63. package/dist/index.js +9054 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/load-scene.d.ts +50 -0
  66. package/dist/load-worker-protocol.d.ts +82 -0
  67. package/dist/loading-wLo8vRbA.js +102 -0
  68. package/dist/loading-wLo8vRbA.js.map +1 -0
  69. package/dist/loading.d.ts +150 -0
  70. package/dist/lod-manifest.d.ts +61 -0
  71. package/dist/lod-scheduler.d.ts +229 -0
  72. package/dist/lod-source.d.ts +207 -0
  73. package/dist/logging-BfPdd7NJ.js +19 -0
  74. package/dist/logging-BfPdd7NJ.js.map +1 -0
  75. package/dist/logging.d.ts +37 -0
  76. package/dist/one-shot-worker-G80btnth.js +664 -0
  77. package/dist/one-shot-worker-G80btnth.js.map +1 -0
  78. package/dist/orientation.d.ts +35 -0
  79. package/dist/ply-header-BwgExCn-.js +125 -0
  80. package/dist/ply-header-BwgExCn-.js.map +1 -0
  81. package/dist/ply-header.d.ts +87 -0
  82. package/dist/radix-sort.d.ts +28 -0
  83. package/dist/radix-sorter.d.ts +46 -0
  84. package/dist/relighting.d.ts +50 -0
  85. package/dist/selection-volume.d.ts +87 -0
  86. package/dist/sh-pack-D5wAe5gg.js +54 -0
  87. package/dist/sh-pack-D5wAe5gg.js.map +1 -0
  88. package/dist/sh-pack.d.ts +49 -0
  89. package/dist/sort-scheduler.d.ts +62 -0
  90. package/dist/sort-worker-protocol.d.ts +37 -0
  91. package/dist/sorter.d.ts +31 -0
  92. package/dist/source-transform.d.ts +57 -0
  93. package/dist/splat-budget-PSojLJPO.js +157 -0
  94. package/dist/splat-budget-PSojLJPO.js.map +1 -0
  95. package/dist/splat-budget.d.ts +404 -0
  96. package/dist/splat-data-BZM5dhhx.js +10 -0
  97. package/dist/splat-data-BZM5dhhx.js.map +1 -0
  98. package/dist/splat-data.d.ts +150 -0
  99. package/dist/splat-depth-pack.d.ts +39 -0
  100. package/dist/splat-mesh-material.d.ts +255 -0
  101. package/dist/splat-mesh-picking.d.ts +87 -0
  102. package/dist/splat-mesh-pool.d.ts +241 -0
  103. package/dist/splat-mesh-types-8QDjTbZO.js +390 -0
  104. package/dist/splat-mesh-types-8QDjTbZO.js.map +1 -0
  105. package/dist/splat-mesh-types.d.ts +390 -0
  106. package/dist/splat-mesh.d.ts +737 -0
  107. package/dist/splat-modifier-stack.d.ts +38 -0
  108. package/dist/splat-modifier.d.ts +156 -0
  109. package/dist/splat-partition.d.ts +37 -0
  110. package/dist/splat-query.d.ts +51 -0
  111. package/dist/splat-scene.d.ts +127 -0
  112. package/dist/splat-sort-bounds.d.ts +11 -0
  113. package/dist/static-lod-splat-mesh.d.ts +53 -0
  114. package/dist/static-lod-worker-D2fyqQZO.js +474 -0
  115. package/dist/static-lod-worker-D2fyqQZO.js.map +1 -0
  116. package/dist/static-lod-worker-protocol.d.ts +37 -0
  117. package/dist/static-lod.d.ts +24 -0
  118. package/dist/storage-attribute-mirror.d.ts +103 -0
  119. package/dist/streamed-splat-mesh-utils.d.ts +146 -0
  120. package/dist/streamed-splat-mesh.d.ts +985 -0
  121. package/dist/unified-splat-renderer.d.ts +205 -0
  122. package/dist/webgpu-limits.d.ts +133 -0
  123. package/dist/work-buffer-gather.d.ts +119 -0
  124. package/dist/work-buffer-material.d.ts +42 -0
  125. package/dist/worker-sorter.d.ts +57 -0
  126. package/dist/xr-view.d.ts +92 -0
  127. package/package.json +135 -21
@@ -0,0 +1,390 @@
1
+ /** Public configuration and result types for SplatMesh. */
2
+ import type * as THREE from 'three/webgpu';
3
+ import type { SplatOrientation } from './orientation';
4
+ import type { SplatModifier } from './splat-modifier';
5
+ import { type SplatDeviceProfile } from './splat-budget';
6
+ import type { SplatPool } from './splat-mesh-pool';
7
+ import type { SplatShInputs, Vec3Uniform } from './splat-mesh-material';
8
+ /** Construction-time projected-footprint policy selected by a streamed format. */
9
+ export type ProjectedFilterProfile = 'default' | 'lcc';
10
+ /**
11
+ * Opaque handle for a range of splats appended to a {@link SplatMesh},
12
+ * used to remove the range again.
13
+ */
14
+ export interface SplatRange {
15
+ /** Number of splats in this range. */
16
+ readonly count: number;
17
+ }
18
+ /**
19
+ * Storage format of a per-splat channel (see {@link SplatMesh.defineChannel}).
20
+ *
21
+ * - `'byte'`: one `Uint8` per splat (`r8unorm`). Compact - a good fit for
22
+ * masks and labels. `ctx.channel(name)` reads it back **normalized** to
23
+ * `[0, 1]`, so a painted `255` reads as `1.0`.
24
+ * - `'float'`: one `Float32` per splat (`r32float`), read back verbatim.
25
+ */
26
+ export type SplatChannelType = 'byte' | 'float';
27
+ /** Options for {@link SplatMesh.defineChannel}. */
28
+ export interface SplatChannelOptions {
29
+ /** Storage format; default `'float'`. */
30
+ type?: SplatChannelType;
31
+ /**
32
+ * Value every splat starts at before any {@link SplatMesh.writeChannel}.
33
+ * Default `0`. For `'byte'` channels this is a raw `0..255` value.
34
+ */
35
+ fill?: number;
36
+ }
37
+ /** The highest SH order this renderer evaluates (3rd → 15 coefficients). */
38
+ export declare const MAX_SH_BANDS = 3;
39
+ /**
40
+ * The contribution-culling profile a mesh will use, given an optional
41
+ * explicit override. Exported so callers that must decide something *before*
42
+ * constructing the mesh - such as whether a streamed scene should fetch its
43
+ * SH at all - agree with what the mesh itself will pick.
44
+ */
45
+ export declare function resolveSplatPerformanceProfile(explicit?: SplatPerformanceProfile, profile?: SplatDeviceProfile | undefined): SplatPerformanceProfile;
46
+ /** Construction options for {@link SplatMesh}. */
47
+ export interface SplatMeshOptions {
48
+ /**
49
+ * Storage for per-splat higher-order SH in a dynamic-capacity pool, in
50
+ * bands (1, 2 or 3 → 3, 8 or 15 coefficients per channel); 0 (default)
51
+ * allocates nothing.
52
+ *
53
+ * Only formats that store SH per splat can fill this - LCC `Quality`, `.rad`, etc.
54
+ * It costs 16 bytes per splat per band-group of four
55
+ * coefficients (64 B/splat at 3 bands), so it is opt-in. On a static mesh
56
+ * this is ignored: packed SH is taken from `source.shPacked` when present,
57
+ * otherwise palette `source.sh` (SOG).
58
+ */
59
+ shBands?: 0 | 1 | 2 | 3;
60
+ /**
61
+ * Minimum interval between WebGPU sorts while the camera moves. When
62
+ * omitted, the interval adapts to the active splat count. Use `0` to sort
63
+ * every changed frame. WebGL worker sorting is unaffected.
64
+ */
65
+ sortIntervalMs?: number;
66
+ /**
67
+ * WebGPU sorter used for A/B validation. Defaults to the proven counting
68
+ * sorter. `'radix'` keeps the fast 24-bit key path; `'exact'` lazy-loads a
69
+ * stable 32-bit Float32-depth radix path that avoids scene-range
70
+ * quantization. The first frames may skip sorting until the module resolves.
71
+ *
72
+ * @experimental Radix strategies may change in a minor release.
73
+ */
74
+ sortStrategy?: SplatSortStrategy;
75
+ /**
76
+ * Render-quality policy. `smooth` rejects negligible projected contributions.
77
+ *
78
+ * The default is device-aware: `smooth` on mobile (where rejecting splats too
79
+ * small or too faint to see is worth far more than it costs), `quality`
80
+ * everywhere else. Passing a value opts out of the detection.
81
+ */
82
+ performanceProfile?: SplatPerformanceProfile;
83
+ /**
84
+ * How far out, in standard deviations, each Gaussian is drawn before it is
85
+ * cut off. Every splat is an alpha-blended quad sized to this radius, so it
86
+ * sets how much each one costs to blend - the dominant cost in a busy view.
87
+ * Lowering it shrinks every quad and clips the faint outer tail of each
88
+ * Gaussian; the falloff within the remaining radius is unchanged.
89
+ *
90
+ * Defaults to `3`, the reference 3DGS rasterizer's radius. Below ~2 the
91
+ * truncation shows as visible splat edges; much above ~5 the extra fill is not
92
+ * worth it. Mobile coverage gaps are handled by `minSplatSizePx` instead of
93
+ * growing every splat.
94
+ */
95
+ maxStdDev?: number;
96
+ /**
97
+ * Floor, in viewport pixels, on each rendered splat's projected quad radius.
98
+ *
99
+ * A screen-space *minimum* size, the counterpart to `maxScreenRadiusPx`'s
100
+ * maximum. When a splat projects smaller than this - because it is distant, or
101
+ * because the whole scene is zoomed out - its quad is grown to this radius and
102
+ * the Gaussian is stretched to fill it (the falloff normalizes to the quad, so
103
+ * no hard edge appears). Splats already larger are untouched, so it costs no
104
+ * extra fill on the near-camera splats that dominate overdraw.
105
+ *
106
+ * This is the fix for the "dark gaps when zoomed out" failure mode: a capture
107
+ * whose finest splats are spaced farther apart than their footprint leaves the
108
+ * background showing between them, and the effect is worst at low resolution -
109
+ * i.e. on a phone. Raising `maxStdDev` also closes the gaps but inflates
110
+ * *every* splat's fragment count by its square, paying the coverage cost on the
111
+ * large splats too; this floor spends it only where a gap can actually open.
112
+ *
113
+ * Defaults to `1.5` px on mobile and `0` (disabled) elsewhere, including
114
+ * fill-constrained desktops. Values around 1–3 px close typical gaps; too
115
+ * large a floor blurs distinct small features into discs, so tune it up from
116
+ * small on the target device. An explicit `0` always disables the floor.
117
+ */
118
+ minSplatSizePx?: number;
119
+ /**
120
+ * Apply the Mip-Splatting 2D antialiasing filter - the screen-space low-pass
121
+ * dilation plus the opacity compensation that conserves each Gaussian's
122
+ * integral, so small/distant splats stop over-brightening. Match the
123
+ * exporter: enable it for scenes trained/exported with antialiasing (the SOG
124
+ * `antialias` meta flag sets this automatically). Defaults to `false` (the
125
+ * classic 3DGS dilation without compensation).
126
+ */
127
+ antialias?: boolean;
128
+ /**
129
+ * Emit splat colors in sRGB (display) space instead of decoding them to the
130
+ * renderer's linear working space. Pair with a renderer that skips output
131
+ * conversion (`outputColorSpace = LinearSRGBColorSpace`, `NoToneMapping`,
132
+ * inline sRGB encode for other materials via `renderer.contextNode`): splats
133
+ * then alpha-composite on gamma-encoded values - the math 3DGS training
134
+ * optimizes against, and what WebGL splat viewers render. Defaults to
135
+ * `false` (linear working-space compositing).
136
+ */
137
+ srgbOutput?: boolean;
138
+ /**
139
+ * Cull any splat whose projected on-screen radius exceeds this many pixels,
140
+ * rendering a hole instead. A physically large splat close to the camera -
141
+ * a coarse merged LOD node (a Spark `.rad` "blob"), or a giant background
142
+ * Gaussian - projects huge while a fine surface splat stays small, so this
143
+ * removes the near-camera blobs without touching detailed geometry. `0` or
144
+ * unset disables it (the default). Baked into the material graph.
145
+ */
146
+ maxSplatScreenRadius?: number;
147
+ /**
148
+ * Foveation band lower bound (px): cull any splat whose projected on-screen
149
+ * radius is *below* this. Paired with {@link maxSplatScreenRadius}, only
150
+ * splats sized `(min, max]` on screen draw. Because a `.rad` LOD tree's node
151
+ * sizes shrink geometrically, exactly one level per view ray lands in the
152
+ * band - near rays on fine leaves, far rays on coarse nodes - giving a
153
+ * camera-distance foveated cut. `0` or unset disables it (the default).
154
+ * Baked into the material graph. See `docs/formats/rad-notes.md` M14.6.
155
+ */
156
+ minSplatScreenRadius?: number;
157
+ /**
158
+ * How a `.rad` foveated mesh picks its per-splat LOD cut:
159
+ * - `'band'` (default): the screen-radius band above
160
+ * ({@link minSplatScreenRadius}, {@link maxSplatScreenRadius}].
161
+ * - `'frontier'`: Spark's exact tree cut - draw splat `i` iff its parent is
162
+ * too big and it is small enough (`parentPixelScale > limit ≥ ownPixelScale`),
163
+ * using per-splat `own_size`/`parent_size`. Full coverage by construction, no
164
+ * band leapfrogging. Baked into the material graph. See `docs/formats/rad-notes.md`.
165
+ * - `'pagetable'`: the {@link StreamedSplatMesh} default for `.rad` - a worker
166
+ * owns the tree traversal and pages only the *selected* frontier into the
167
+ * pool (Spark's selected-index model), so the whole splat budget buys
168
+ * on-screen detail. Requires the streamed `.rad` machinery; on a plain
169
+ * `SplatMesh` it has no worker to drive it.
170
+ *
171
+ * @experimental `.rad` foveation option; may change in a minor release.
172
+ */
173
+ foveationMode?: 'band' | 'frontier' | 'pagetable';
174
+ /**
175
+ * Target on-screen size (px) for the frontier / page-table cut: it keeps one
176
+ * LOD level per view ray whose projected node size is about this. Larger =
177
+ * coarser/fewer splats, smaller = finer/denser. Default
178
+ * {@link DEFAULT_FOVEATION_TARGET_PX} (1, matching Spark's `lodRenderScale`),
179
+ * so the draw budget rather than the cut size is what bounds detail. Raise it
180
+ * to trade sharpness for fill rate on weak GPUs.
181
+ * Acts as the *finest* bound: the adaptive limit coarsens above it to hold the
182
+ * draw budget but never dips below it.
183
+ *
184
+ * @experimental `.rad` foveation option; may change in a minor release.
185
+ */
186
+ foveationTargetPx?: number;
187
+ /**
188
+ * Target upper bound on the number of splats the frontier cut *draws*
189
+ * (Spark's `maxSplats`). Each reschedule the cut's `pixelScaleLimit`
190
+ * self-adjusts - coarsening when the estimated drawn count exceeds this - so
191
+ * frame cost stays bounded as detail streams in. Default
192
+ * {@link DEFAULT_FOVEATION_DRAW_BUDGET}. Only used in `'frontier'` mode.
193
+ *
194
+ * @experimental `.rad` foveation option; may change in a minor release.
195
+ */
196
+ foveationDrawBudget?: number;
197
+ /**
198
+ * Cap on a rendered splat's major/minor axis ratio (`0`/unset = off). A very
199
+ * anisotropic Gaussian (a flat 3DGS disk edge-on, or an expansion-enlarged
200
+ * coarse LOD node) otherwise projects to a long needle; this bounds its drawn
201
+ * length to `maxSplatAspect`× its width. Baked into the material graph.
202
+ *
203
+ * @experimental `.rad` foveation option; may change in a minor release.
204
+ */
205
+ maxSplatAspect?: number;
206
+ /**
207
+ * Spark's LOD alpha encoding (`.rad`): the stored opacity is `alpha/2` so the
208
+ * shader recovers `alpha ∈ [0,2]`, and `alpha > 1` marks a merged node rendered
209
+ * with a grown σ-cutoff + super-Gaussian falloff. Set for foveated `.rad`.
210
+ *
211
+ * @experimental `.rad` foveation option; may change in a minor release.
212
+ */
213
+ lodAlpha?: boolean;
214
+ /**
215
+ * How the scene is oriented into the three.js Y-up world. `'y-up'` (default)
216
+ * normalizes every known format to Y-up - 3DGS Y-down formats
217
+ * (PLY/`.splat`/`.ksplat`/SOG) are flipped 180° about X; SPZ/`.rad` are
218
+ * already Y-up; LCC keeps its own Z-up→Y-up matrix. `'source'` applies no
219
+ * cosmetic flip and renders in the data frame (raw Spark / mkkellogg parity);
220
+ * LCC still self-orients (that is format semantics, not part of the switch).
221
+ *
222
+ * For a static mesh the flip is chosen from {@link SplatData.sourceFormat}
223
+ * (stamped by the loaders); a dynamic-capacity mesh carries no format, so its
224
+ * host applies {@link yUpTransformForFormat} itself. See {@link SplatOrientation}.
225
+ */
226
+ orientation?: SplatOrientation;
227
+ /**
228
+ * GPU storage type for the pool's continuous float textures (`centers` and
229
+ * `covarianceA`). `'float16'` uploads them as `rgba16float` (~16 B/splat
230
+ * saved vs the default). CPU backing stays float32 (sorter, query, writes).
231
+ *
232
+ * `covarianceB` is always float32: it packs integer IDs (SOG palette labels,
233
+ * RAD frontier parents) that half floats cannot represent exactly above
234
+ * 2048. Colors and packed SH are unchanged. Construction-time only.
235
+ */
236
+ poolFloatTextures?: 'float32' | 'float16';
237
+ /**
238
+ * An existing pool to draw from instead of allocating one.
239
+ *
240
+ * Several meshes sharing a pool share its memory envelope: rows go to
241
+ * whichever mesh needs them, so a mesh the camera is near can hold far more
242
+ * than an even split would give it, and one that is far away holds almost
243
+ * nothing - without every mesh having reserved a private ceiling up front.
244
+ * This is the multi-mesh analogue of a single streamed mesh's LOD budget.
245
+ *
246
+ * The pool is *not* owned by the mesh: {@link SplatMesh.dispose} releases the
247
+ * mesh's rows and leaves the textures alone, so the pool's creator disposes
248
+ * it once every tenant is gone. `capacity` on the source is then only used
249
+ * for the mesh's own draw list, not to size storage.
250
+ *
251
+ * Sharing costs a whole-pool stall when the pool fragments - see
252
+ * {@link SplatMesh.compact}.
253
+ */
254
+ pool?: SplatPool;
255
+ /**
256
+ * The device's `maxTextureDimension2D`, forwarded to a pool this mesh
257
+ * allocates for itself so an over-tall pool fails at construction with a
258
+ * readable error instead of at first draw. Pass `deviceMaxTextureSize(renderer)`.
259
+ *
260
+ * Ignored when {@link SplatMeshOptions.pool} supplies the pool - that pool
261
+ * was already checked when its creator built it.
262
+ */
263
+ maxTextureSize?: number;
264
+ }
265
+ /** Available WebGPU depth-sort implementations. */
266
+ export type SplatSortStrategy = 'counting' | 'radix' | 'exact';
267
+ /** Controls optional work during a per-frame source update. */
268
+ export interface SplatUpdateOptions {
269
+ /** Leave sorting to {@link UnifiedSplatRenderer}; uploads and LOD state still update. */
270
+ sort?: boolean;
271
+ }
272
+ /**
273
+ * Read-only GPU-facing view of a mesh's current active pool. It is consumed by
274
+ * the M15.4 unified gather path; streamed meshes expose their current LOD cut
275
+ * through the same view because they inherit {@link SplatMesh}.
276
+ *
277
+ * @experimental May change in a minor release.
278
+ */
279
+ export interface UnifiedSourceView {
280
+ /** Total addressable pool slots. */
281
+ readonly capacity: number;
282
+ /** Pool indices of active splats, packed from zero. */
283
+ readonly sourceIndex: THREE.StorageBufferAttribute;
284
+ /** Active entries at the front of {@link sourceIndex}. */
285
+ readonly activeCount: number;
286
+ /** Local-space centers, RGBA32F and pool-indexed. */
287
+ readonly centersTexture: THREE.DataTexture;
288
+ /** Source display color and opacity, RGBA8 and pool-indexed. */
289
+ readonly colorsTexture: THREE.DataTexture;
290
+ /** Upper covariance rows, RGBA32F and pool-indexed. */
291
+ readonly covarianceATexture: THREE.DataTexture;
292
+ /** Final covariance row, RGBA32F and pool-indexed. */
293
+ readonly covarianceBTexture: THREE.DataTexture;
294
+ /** Centers texture row width. */
295
+ readonly dataTextureWidth: number;
296
+ /** Current source-local → world transform. */
297
+ readonly matrixWorld: THREE.Matrix4;
298
+ /** Conservative world-space bound for depth quantization. */
299
+ readonly worldBounds: THREE.Sphere;
300
+ /** Higher-order color data resolved by the gather pass when present. */
301
+ readonly sh: SplatShInputs | null;
302
+ /** Effect hooks and their source-local data channels. */
303
+ readonly modifiers: readonly SplatModifier[];
304
+ /**
305
+ * True when this source is itself a unified pool with per-source placement
306
+ * (`SplatScene`). The gather path cannot resolve nested placement, so
307
+ * `UnifiedSplatRenderer` rejects these sources.
308
+ */
309
+ readonly hasSourcePlacement: boolean;
310
+ readonly channels: ReadonlyMap<string, {
311
+ texture: THREE.DataTexture;
312
+ }>;
313
+ /** Same uniform node the source graph updates each frame. */
314
+ readonly localCameraPosition: Vec3Uniform;
315
+ /** Changes whenever a modifier graph must be rebuilt. */
316
+ readonly graphRevision: number;
317
+ /** Whether this source intentionally composites in display (sRGB) space. */
318
+ readonly srgbOutput: boolean;
319
+ /** Shared draw-path settings that must agree across unified sources. */
320
+ readonly maxStdDev: number;
321
+ /** Screen-space minimum splat radius, px (0 = off). */
322
+ readonly minSplatSizePx: number;
323
+ readonly antialias: boolean;
324
+ /** Construction-time projected-footprint policy shared by one unified pass. */
325
+ readonly projectedFilterProfile: ProjectedFilterProfile;
326
+ /**
327
+ * Whether this source stores Spark LOD alpha (`alpha ÷ 2`, `.rad`). The
328
+ * gather recovers the full `alpha ∈ [0,2]`; the draw material then treats
329
+ * `alpha > 1` as a merged node. Per source, not a compatibility field - a
330
+ * scene may mix `.rad` and non-`.rad` sources.
331
+ */
332
+ readonly lodAlpha: boolean;
333
+ /** Increments whenever pool-backed data or active residency changes. */
334
+ readonly contentRevision: number;
335
+ }
336
+ /** Quality-compatible rendering or smoother contribution-culling rendering. */
337
+ export type SplatPerformanceProfile = 'quality' | 'smooth';
338
+ /**
339
+ * Options for {@link SplatMesh.pick}.
340
+ *
341
+ * Picking returns the selected splat's rendered center plane (depth-tested
342
+ * Gaussian coverage), not a persistent splat identifier or a collision mesh.
343
+ */
344
+ export interface SplatPickOptions {
345
+ /**
346
+ * Minimum Gaussian opacity (after falloff × splat alpha) for a fragment to
347
+ * count as a hit. Default `0.1`.
348
+ */
349
+ alphaThreshold?: number;
350
+ }
351
+ /**
352
+ * Result of a successful {@link SplatMesh.pick}.
353
+ *
354
+ * The point lies on the frontmost splat's billboard plane at the picked
355
+ * pixel - suitable for click-to-focus and placement anchors, not physics.
356
+ */
357
+ export interface SplatPickResult {
358
+ /** Hit position in world space. */
359
+ readonly point: THREE.Vector3;
360
+ /** Distance from the camera position to {@link point}. */
361
+ readonly distance: number;
362
+ }
363
+ /**
364
+ * Result of {@link SplatMesh.queryNearest}: the resident splat center closest
365
+ * to the query point, in world space.
366
+ */
367
+ export interface SplatNearestResult {
368
+ /** The splat's center, in world space. */
369
+ readonly point: THREE.Vector3;
370
+ /** World-space distance from the query point to {@link point}. */
371
+ readonly distance: number;
372
+ }
373
+ /** Result of a successful synchronous {@link SplatMesh.queryRay}. */
374
+ export interface SplatRayResult {
375
+ /** Resident splat center in world space. */
376
+ readonly point: THREE.Vector3;
377
+ /** Distance along the ray from its origin to the center's closest plane. */
378
+ readonly distance: number;
379
+ }
380
+ /**
381
+ * Result of {@link SplatMesh.queryHeight}: the supporting surface found beneath
382
+ * the query point (the highest resident splat within the drop and horizontal
383
+ * radius), in world space.
384
+ */
385
+ export interface SplatHeightResult {
386
+ /** The supporting splat's center, in world space. */
387
+ readonly point: THREE.Vector3;
388
+ /** How far below the query point the surface sits (world units, ≥ 0). */
389
+ readonly drop: number;
390
+ }