@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.
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/THIRD_PARTY_NOTICES.md +86 -0
- package/dist/budget-governor.d.ts +154 -0
- package/dist/camera-budget-governor.d.ts +234 -0
- package/dist/chunk-cache-budget.d.ts +156 -0
- package/dist/chunk-fetch-scheduler.d.ts +168 -0
- package/dist/chunk-loader.d.ts +85 -0
- package/dist/compute-sorter.d.ts +131 -0
- package/dist/create-splat-renderer.d.ts +99 -0
- package/dist/dataset-source.d.ts +55 -0
- package/dist/depth-of-field.d.ts +73 -0
- package/dist/effects.d.ts +337 -0
- package/dist/effects.js +180 -0
- package/dist/effects.js.map +1 -0
- package/dist/formats/ksplat/index.d.ts +8 -0
- package/dist/formats/ksplat/parse-ksplat.d.ts +9 -0
- package/dist/formats/ksplat.js +167 -0
- package/dist/formats/ksplat.js.map +1 -0
- package/dist/formats/lcc/collision-mesh.d.ts +37 -0
- package/dist/formats/lcc/collision-partition.d.ts +35 -0
- package/dist/formats/lcc/index.d.ts +16 -0
- package/dist/formats/lcc/lcc.d.ts +28 -0
- package/dist/formats/lcc/lcc2-transform.d.ts +18 -0
- package/dist/formats/lcc/lcc2.d.ts +4 -0
- package/dist/formats/lcc/parse-collision-lci.d.ts +28 -0
- package/dist/formats/lcc/parse-lcc.d.ts +177 -0
- package/dist/formats/lcc/parse-mesh-ply.d.ts +21 -0
- package/dist/formats/lcc.js +969 -0
- package/dist/formats/lcc.js.map +1 -0
- package/dist/formats/ply/index.d.ts +12 -0
- package/dist/formats/ply/parse-compressed-ply.d.ts +16 -0
- package/dist/formats/ply/parse-splat-ply.d.ts +46 -0
- package/dist/formats/ply.js +230 -0
- package/dist/formats/ply.js.map +1 -0
- package/dist/formats/rad/frontier-worker-protocol.d.ts +177 -0
- package/dist/formats/rad/index.d.ts +10 -0
- package/dist/formats/rad/parse-rad.d.ts +88 -0
- package/dist/formats/rad/rad-column-decoders.d.ts +15 -0
- package/dist/formats/rad/rad-foveated-source.d.ts +71 -0
- package/dist/formats/rad/rad-parent-sizes.d.ts +45 -0
- package/dist/formats/rad/rad.d.ts +132 -0
- package/dist/formats/rad.js +1098 -0
- package/dist/formats/rad.js.map +1 -0
- package/dist/formats/sog/index.d.ts +9 -0
- package/dist/formats/sog/parse-sog.d.ts +45 -0
- package/dist/formats/sog.js +257 -0
- package/dist/formats/sog.js.map +1 -0
- package/dist/formats/splat/index.d.ts +8 -0
- package/dist/formats/splat/parse-splat.d.ts +10 -0
- package/dist/formats/splat.js +39 -0
- package/dist/formats/splat.js.map +1 -0
- package/dist/formats/spz/index.d.ts +8 -0
- package/dist/formats/spz/parse-spz.d.ts +10 -0
- package/dist/formats/spz.js +218 -0
- package/dist/formats/spz.js.map +1 -0
- package/dist/frontier-worker-CAnK_Cmu.js +566 -0
- package/dist/frontier-worker-CAnK_Cmu.js.map +1 -0
- package/dist/half-float-Cs4FDq8Q.js +30 -0
- package/dist/half-float-Cs4FDq8Q.js.map +1 -0
- package/dist/half-float.d.ts +15 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +9054 -0
- package/dist/index.js.map +1 -0
- package/dist/load-scene.d.ts +50 -0
- package/dist/load-worker-protocol.d.ts +82 -0
- package/dist/loading-wLo8vRbA.js +102 -0
- package/dist/loading-wLo8vRbA.js.map +1 -0
- package/dist/loading.d.ts +150 -0
- package/dist/lod-manifest.d.ts +61 -0
- package/dist/lod-scheduler.d.ts +229 -0
- package/dist/lod-source.d.ts +207 -0
- package/dist/logging-BfPdd7NJ.js +19 -0
- package/dist/logging-BfPdd7NJ.js.map +1 -0
- package/dist/logging.d.ts +37 -0
- package/dist/one-shot-worker-G80btnth.js +664 -0
- package/dist/one-shot-worker-G80btnth.js.map +1 -0
- package/dist/orientation.d.ts +35 -0
- package/dist/ply-header-BwgExCn-.js +125 -0
- package/dist/ply-header-BwgExCn-.js.map +1 -0
- package/dist/ply-header.d.ts +87 -0
- package/dist/radix-sort.d.ts +28 -0
- package/dist/radix-sorter.d.ts +46 -0
- package/dist/relighting.d.ts +50 -0
- package/dist/selection-volume.d.ts +87 -0
- package/dist/sh-pack-D5wAe5gg.js +54 -0
- package/dist/sh-pack-D5wAe5gg.js.map +1 -0
- package/dist/sh-pack.d.ts +49 -0
- package/dist/sort-scheduler.d.ts +62 -0
- package/dist/sort-worker-protocol.d.ts +37 -0
- package/dist/sorter.d.ts +31 -0
- package/dist/source-transform.d.ts +57 -0
- package/dist/splat-budget-PSojLJPO.js +157 -0
- package/dist/splat-budget-PSojLJPO.js.map +1 -0
- package/dist/splat-budget.d.ts +404 -0
- package/dist/splat-data-BZM5dhhx.js +10 -0
- package/dist/splat-data-BZM5dhhx.js.map +1 -0
- package/dist/splat-data.d.ts +150 -0
- package/dist/splat-depth-pack.d.ts +39 -0
- package/dist/splat-mesh-material.d.ts +255 -0
- package/dist/splat-mesh-picking.d.ts +87 -0
- package/dist/splat-mesh-pool.d.ts +241 -0
- package/dist/splat-mesh-types-8QDjTbZO.js +390 -0
- package/dist/splat-mesh-types-8QDjTbZO.js.map +1 -0
- package/dist/splat-mesh-types.d.ts +390 -0
- package/dist/splat-mesh.d.ts +737 -0
- package/dist/splat-modifier-stack.d.ts +38 -0
- package/dist/splat-modifier.d.ts +156 -0
- package/dist/splat-partition.d.ts +37 -0
- package/dist/splat-query.d.ts +51 -0
- package/dist/splat-scene.d.ts +127 -0
- package/dist/splat-sort-bounds.d.ts +11 -0
- package/dist/static-lod-splat-mesh.d.ts +53 -0
- package/dist/static-lod-worker-D2fyqQZO.js +474 -0
- package/dist/static-lod-worker-D2fyqQZO.js.map +1 -0
- package/dist/static-lod-worker-protocol.d.ts +37 -0
- package/dist/static-lod.d.ts +24 -0
- package/dist/storage-attribute-mirror.d.ts +103 -0
- package/dist/streamed-splat-mesh-utils.d.ts +146 -0
- package/dist/streamed-splat-mesh.d.ts +985 -0
- package/dist/unified-splat-renderer.d.ts +205 -0
- package/dist/webgpu-limits.d.ts +133 -0
- package/dist/work-buffer-gather.d.ts +119 -0
- package/dist/work-buffer-material.d.ts +42 -0
- package/dist/worker-sorter.d.ts +57 -0
- package/dist/xr-view.d.ts +92 -0
- package/package.json +135 -21
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
import { type SplatChannelOptions, type SplatHeightResult, type SplatMeshOptions, type SplatNearestResult, type SplatPerformanceProfile, type SplatPickOptions, type SplatPickResult, type SplatRayResult, type SplatRange, type SplatUpdateOptions, type UnifiedSourceView } from './splat-mesh-types';
|
|
2
|
+
export * from './splat-mesh-types';
|
|
3
|
+
import * as THREE from 'three/webgpu';
|
|
4
|
+
import type { SplatData } from './splat-data';
|
|
5
|
+
import { type SplatOrientation } from './orientation';
|
|
6
|
+
import type { SplatModifier } from './splat-modifier';
|
|
7
|
+
import { type PerSourceSortTransform } from './compute-sorter';
|
|
8
|
+
import { type DepthOfFieldSettings } from './depth-of-field';
|
|
9
|
+
import { type RelightingSettings, type RelightingUniforms } from './relighting';
|
|
10
|
+
import { type SplatPoolRange, type SplatPoolTenant } from './splat-mesh-pool';
|
|
11
|
+
/**
|
|
12
|
+
* Renders a set of 3D Gaussians with three.js's WebGPURenderer.
|
|
13
|
+
*
|
|
14
|
+
* Rendering technique: EWA splatting per the 3DGS paper (Kerbl et al.,
|
|
15
|
+
* SIGGRAPH 2023). Each splat is an instanced quad; the vertex stage projects
|
|
16
|
+
* the splat's 3D covariance to a screen-space ellipse (the same math used by
|
|
17
|
+
* antimatter15/splat and PlayCanvas, both MIT) and the fragment stage applies
|
|
18
|
+
* the Gaussian falloff. Shaders are written in TSL, so three.js compiles
|
|
19
|
+
* them to WGSL on WebGPU and to GLSL on the automatic WebGL2 fallback.
|
|
20
|
+
*
|
|
21
|
+
* Splat data lives in pool data textures, indexed through one small
|
|
22
|
+
* per-instance `splatIndex` attribute. Correct alpha blending needs
|
|
23
|
+
* back-to-front ordering, so the indices are depth-sorted whenever the
|
|
24
|
+
* camera moves - only that index buffer is rewritten, never the splat data
|
|
25
|
+
* itself. On the WebGPU backend the sort runs in TSL compute passes
|
|
26
|
+
* (`ComputeSorter`); the WebGL2 fallback uses a Web Worker (`WorkerSorter`).
|
|
27
|
+
*
|
|
28
|
+
* Two construction modes:
|
|
29
|
+
*
|
|
30
|
+
* - `new SplatMesh(data)` - static: capacity equals the data's count and
|
|
31
|
+
* the whole scene is resident. Works on WebGPU and the WebGL2 fallback,
|
|
32
|
+
* including SOG view-dependent color (shN).
|
|
33
|
+
* - `new SplatMesh({ capacity })` - dynamic: an empty pool for up to
|
|
34
|
+
* `capacity` splats. Content is managed with {@link appendRange} /
|
|
35
|
+
* {@link removeRange} (ranges are row-aligned in the pool textures, so
|
|
36
|
+
* uploads are rectangular). This mode is the substrate for LOD
|
|
37
|
+
* streaming and works on both backends - the WebGL2 fallback sorts the
|
|
38
|
+
* active pool spans on the CPU. shN data on appended ranges is ignored
|
|
39
|
+
* (palettes are per-file and cannot be merged here).
|
|
40
|
+
*/
|
|
41
|
+
export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
|
|
42
|
+
private static readonly DATA_TEXTURE_WIDTH;
|
|
43
|
+
/**
|
|
44
|
+
* The storage this mesh draws from. Held rather than inlined because a
|
|
45
|
+
* splat's draw identity is already independent of its pool slot (`splatIndex`
|
|
46
|
+
* indirects every instance), so the pool is a separable concern - see
|
|
47
|
+
* {@link SplatPool}.
|
|
48
|
+
*/
|
|
49
|
+
private readonly pool;
|
|
50
|
+
/** False when the pool was supplied by the caller, and so outlives this mesh. */
|
|
51
|
+
private readonly ownsPool;
|
|
52
|
+
private readonly splatIndexAttribute;
|
|
53
|
+
private readonly sourceIndexAttribute;
|
|
54
|
+
private dataTextures;
|
|
55
|
+
/**
|
|
56
|
+
* Pool index → this mesh's packed active-list slot. Lives on the pool: it is
|
|
57
|
+
* keyed by pool index, so a per-mesh copy would span the whole pool and
|
|
58
|
+
* several meshes sharing one would each pay for all of it. A row has one
|
|
59
|
+
* owner at a time, so the single array is unambiguous.
|
|
60
|
+
*/
|
|
61
|
+
private get activeSlotByPoolIndex();
|
|
62
|
+
/** Rows in the pool backing this mesh. */
|
|
63
|
+
private get poolRows();
|
|
64
|
+
private get centersTexture();
|
|
65
|
+
private get backing();
|
|
66
|
+
private get freeRowSpans();
|
|
67
|
+
private set freeRowSpans(value);
|
|
68
|
+
private get poolFloatTextures();
|
|
69
|
+
/** Bands of per-splat (non-palette) SH this pool stores; 0 when disabled. */
|
|
70
|
+
private get packedShBands();
|
|
71
|
+
private get shPackedTextures();
|
|
72
|
+
/** True when constructed from a complete SplatData (single fixed range). */
|
|
73
|
+
private readonly isStatic;
|
|
74
|
+
/** Constructor-created range, retained for static in-place LOD replacement. */
|
|
75
|
+
private staticRange;
|
|
76
|
+
private readonly shEnabled;
|
|
77
|
+
/**
|
|
78
|
+
* Dequantization range for the packed SH, shared by every coefficient.
|
|
79
|
+
* Uniforms rather than constants: the first chunk to arrive supplies it,
|
|
80
|
+
* long after the material is built.
|
|
81
|
+
*/
|
|
82
|
+
private readonly shRange;
|
|
83
|
+
/** Set once the first packed-SH chunk has supplied {@link shRange}. */
|
|
84
|
+
private shRangeSet;
|
|
85
|
+
/** Whether SH-less rows were neutral-filled before the scene range locked. */
|
|
86
|
+
private wrotePreLockNeutralSh;
|
|
87
|
+
private readonly ranges;
|
|
88
|
+
private activeCount;
|
|
89
|
+
/**
|
|
90
|
+
* Bumped whenever the resident splat set or its positions change (activate,
|
|
91
|
+
* deactivate, compact) - the signal the lazy spatial-query grid rebuilds on.
|
|
92
|
+
* A depth re-sort reorders the active list but changes neither, so it does
|
|
93
|
+
* not bump this. See {@link queryNearest}.
|
|
94
|
+
*/
|
|
95
|
+
private queryEpoch;
|
|
96
|
+
private queryGrid;
|
|
97
|
+
private queryGridEpoch;
|
|
98
|
+
/**
|
|
99
|
+
* True when the shared depth-order buffer holds a *secondary* view's order
|
|
100
|
+
* (left by {@link renderView}), so the next primary {@link update} must
|
|
101
|
+
* re-sort even if its camera has not moved. See {@link renderView} (M10).
|
|
102
|
+
*/
|
|
103
|
+
private orderIsForeign;
|
|
104
|
+
/**
|
|
105
|
+
* True while the draw list holds a CPU-worker depth permutation rather than
|
|
106
|
+
* the identity active order. Active-list mutations must then resync the
|
|
107
|
+
* whole drawn prefix instead of patching slots: patching a *permutation*
|
|
108
|
+
* with identity active-list values creates duplicates and drops live splats
|
|
109
|
+
* for every frame until the worker's next order lands (the WebGL2 streamed
|
|
110
|
+
* flicker, ROADMAP L5).
|
|
111
|
+
*/
|
|
112
|
+
private drawListSorted;
|
|
113
|
+
/** CPU timings from the most recent render-preparation update (reset in place per frame). */
|
|
114
|
+
private readonly updateTimings;
|
|
115
|
+
protected readonly localBounds: THREE.Box3;
|
|
116
|
+
protected readonly boundingSphereLocal: THREE.Sphere;
|
|
117
|
+
protected boundsDirty: boolean;
|
|
118
|
+
/** Cached {@link getUnifiedSourceView} result; see the invalidation checks there. */
|
|
119
|
+
private cachedUnifiedView;
|
|
120
|
+
private readonly cachedUnifiedViewMatrixWorld;
|
|
121
|
+
private readonly cachedUnifiedViewLocalBounds;
|
|
122
|
+
private readonly cachedUnifiedViewWorldBounds;
|
|
123
|
+
/** Row spans written since the last flush, awaiting GPU upload. */
|
|
124
|
+
private pendingUploadRows;
|
|
125
|
+
/** Row spans awaiting mirroring to the CPU sort worker (WebGL2 path). */
|
|
126
|
+
private workerDirtyRows;
|
|
127
|
+
/** Persistent copy sources avoid allocating four temporary textures per upload region. */
|
|
128
|
+
private readonly uploadStaging;
|
|
129
|
+
/** Reusable float→half encode buffers keyed like {@link uploadStaging}. */
|
|
130
|
+
private readonly halfEncodeBuffers;
|
|
131
|
+
/** Opt-in per-splat channels by name; see {@link defineChannel}. */
|
|
132
|
+
private readonly channels;
|
|
133
|
+
/** Created on the first update, when the renderer backend is known. */
|
|
134
|
+
private sorter;
|
|
135
|
+
/**
|
|
136
|
+
* Set by a unified {@link SplatScene} subclass before the first sort: makes
|
|
137
|
+
* the WebGPU sorter transform each splat's center to world space by its
|
|
138
|
+
* source's matrix before measuring depth. `null` for an ordinary mesh, whose
|
|
139
|
+
* sorter is byte-identical to before. See `source-transform.ts`.
|
|
140
|
+
*/
|
|
141
|
+
protected perSourceSort: PerSourceSortTransform | null;
|
|
142
|
+
/** Effect hooks folded into the vertex graph; see {@link modifiers}. */
|
|
143
|
+
private modifierList;
|
|
144
|
+
/** Increments when a unified gather must rebuild its source graph. */
|
|
145
|
+
private graphRevision;
|
|
146
|
+
/** Version for unified work-buffer caching; source pool writes are observable. */
|
|
147
|
+
private contentRevision;
|
|
148
|
+
/** Material-graph inputs kept for rebuilds when the modifier list changes. */
|
|
149
|
+
private materialInputs;
|
|
150
|
+
/** Projection focal lengths in pixels; updated every frame. */
|
|
151
|
+
private readonly focal;
|
|
152
|
+
/** Drawing-buffer size in pixels; updated every frame. */
|
|
153
|
+
private readonly viewport;
|
|
154
|
+
/** Camera position in this mesh's local space, for SH view dependence. */
|
|
155
|
+
private readonly localCameraPosition;
|
|
156
|
+
/**
|
|
157
|
+
* Frontier-cut limit for `foveationMode: 'frontier'` - the maximum
|
|
158
|
+
* `own_size / distance` a splat may have and still draw (Spark's
|
|
159
|
+
* `pixelScaleLimit`). Set each frame to `foveationTargetPx / focalPx` so the
|
|
160
|
+
* cut targets a fixed on-screen size. Unused in `'band'` mode.
|
|
161
|
+
*/
|
|
162
|
+
private readonly pixelScaleLimit;
|
|
163
|
+
/**
|
|
164
|
+
* Core projected-2D depth of field (live uniforms). Aperture `0` disables.
|
|
165
|
+
* Prefer this over the M13 `depthOfFieldPreset` modifier for camera DoF.
|
|
166
|
+
*/
|
|
167
|
+
private readonly dofFocusDistance;
|
|
168
|
+
private readonly dofAperture;
|
|
169
|
+
/**
|
|
170
|
+
* Proxy-mesh screen-space relighting (live uniforms + map). `blend === 0`
|
|
171
|
+
* disables. Swapping the map rebuilds the material graph once.
|
|
172
|
+
*/
|
|
173
|
+
private readonly relightPlaceholder;
|
|
174
|
+
private relightMap;
|
|
175
|
+
private readonly relightBlend;
|
|
176
|
+
private readonly relightBrightness;
|
|
177
|
+
private readonly relightBackground;
|
|
178
|
+
private readonly relightSoftness;
|
|
179
|
+
/**
|
|
180
|
+
* Live screen-radius band bounds (px), seeded from
|
|
181
|
+
* {@link SplatMeshOptions.minSplatScreenRadius} / `maxSplatScreenRadius` and
|
|
182
|
+
* moved by {@link setScreenRadiusBand} - so a foveated mesh can widen the band
|
|
183
|
+
* as its LOD cut refines rather than culling the detail the refinement bought.
|
|
184
|
+
*/
|
|
185
|
+
private readonly screenBandMin;
|
|
186
|
+
private readonly screenBandMax;
|
|
187
|
+
/** The pick pass and everything it allocates; see `splat-mesh-picking.ts`. */
|
|
188
|
+
private readonly picker;
|
|
189
|
+
/** Visibility supplied by an owning unified renderer for source-only picks. */
|
|
190
|
+
private unifiedPickVisibility;
|
|
191
|
+
/** Set by {@link dispose}; subclasses use it to drop late async results. */
|
|
192
|
+
protected disposed: boolean;
|
|
193
|
+
/** The renderer this mesh last updated with, so {@link dispose} can free
|
|
194
|
+
* storage-attribute GPU buffers that never sat in a geometry. */
|
|
195
|
+
private lastRenderer;
|
|
196
|
+
private readonly currentModelView;
|
|
197
|
+
/** Initialized to an impossible matrix so the first frame always sorts. */
|
|
198
|
+
private readonly lastSortedModelView;
|
|
199
|
+
private readonly sortScheduler;
|
|
200
|
+
/** One-frame queue-headroom hint used before a staged atomic commit. */
|
|
201
|
+
private deferSortRequestOnce;
|
|
202
|
+
/** Bumped by every active-list mutation; identifies a draw list. */
|
|
203
|
+
private activeListVersion;
|
|
204
|
+
/** The active list the current depth order was built from. */
|
|
205
|
+
private sortedActiveListVersion;
|
|
206
|
+
private readonly sortStrategy;
|
|
207
|
+
/** Resolved only when `sortStrategy === 'radix'`; see {@link ensureRadixSorter}. */
|
|
208
|
+
private RadixSorterCtor;
|
|
209
|
+
private radixSorterLoad;
|
|
210
|
+
private performanceProfileValue;
|
|
211
|
+
/** Gaussian cutoff radius in σ; baked into the material graph. */
|
|
212
|
+
private maxStdDevValue;
|
|
213
|
+
/** Screen-space minimum splat radius in px; baked into the material graph. */
|
|
214
|
+
private readonly minSplatSizePx;
|
|
215
|
+
/** Mip-Splatting 2D antialiasing filter; baked into the material graph. */
|
|
216
|
+
private readonly antialias;
|
|
217
|
+
/** Format-selected low-pass and opacity-compensation policy. */
|
|
218
|
+
private readonly projectedFilterProfile;
|
|
219
|
+
/** Emit sRGB colors for gamma-space compositing; baked into the material graph. */
|
|
220
|
+
private readonly srgbOutput;
|
|
221
|
+
/** Screen-radius cull threshold in px (0 = off); baked into the material graph. */
|
|
222
|
+
private readonly maxSplatScreenRadius;
|
|
223
|
+
private readonly minSplatScreenRadius;
|
|
224
|
+
/** `.rad` foveation cut selector; baked into the material graph. */
|
|
225
|
+
private readonly foveationMode;
|
|
226
|
+
/** Finest on-screen node size (px) the frontier cut may request. */
|
|
227
|
+
private readonly foveationTargetPx;
|
|
228
|
+
/** Target upper bound on the frontier cut's drawn-splat count. */
|
|
229
|
+
private readonly foveationDrawBudget;
|
|
230
|
+
/** Rendered major/minor axis-ratio cap (0 = off); baked into the material graph. */
|
|
231
|
+
private readonly maxSplatAspect;
|
|
232
|
+
/** Spark LOD alpha encoding + merged-node rendering; baked into the material. */
|
|
233
|
+
private readonly lodAlpha;
|
|
234
|
+
/** Live frontier limit in px; self-adjusts between `foveationTargetPx` (finest)
|
|
235
|
+
* and coarser to hold `foveationDrawBudget`. Feeds `pixelScaleLimit` each frame. */
|
|
236
|
+
private foveationLimitPx;
|
|
237
|
+
/** Throttle timestamp for the adaptive-limit estimate (ms, `performance.now`). */
|
|
238
|
+
private lastFoveationAdaptAt;
|
|
239
|
+
/** Y-up normalization policy; drives the static self-orientation below. */
|
|
240
|
+
readonly orientation: SplatOrientation;
|
|
241
|
+
/** Set once the pool has been checked against the device texture limit. */
|
|
242
|
+
private textureLimitChecked;
|
|
243
|
+
constructor(source: SplatData | {
|
|
244
|
+
capacity: number;
|
|
245
|
+
}, options?: SplatMeshOptions);
|
|
246
|
+
/**
|
|
247
|
+
* Higher-order SH bands this mesh actually renders (0 when it has none),
|
|
248
|
+
* whatever the source: a static mesh's palette shN, or per-splat SH in a
|
|
249
|
+
* dynamic pool. Reflects what was resolved at construction, not what was
|
|
250
|
+
* asked for - useful for a debug readout.
|
|
251
|
+
*/
|
|
252
|
+
get shBands(): number;
|
|
253
|
+
/**
|
|
254
|
+
* The contribution-culling profile currently in effect (resolved at
|
|
255
|
+
* construction, or the last {@link setPerformanceProfile} value).
|
|
256
|
+
*/
|
|
257
|
+
get performanceProfile(): SplatPerformanceProfile;
|
|
258
|
+
/**
|
|
259
|
+
* Changes contribution culling without changing scene data or persisted
|
|
260
|
+
* settings.
|
|
261
|
+
*
|
|
262
|
+
* Mutator convention: state that is plain data is a property (get/set pair);
|
|
263
|
+
* a `setX` method exists where the write has behavior - this one rebuilds the
|
|
264
|
+
* material graph, {@link StreamedSplatMesh.setBudget} returns its clamp.
|
|
265
|
+
* Every mutator has a matching readable property.
|
|
266
|
+
*/
|
|
267
|
+
setPerformanceProfile(profile: SplatPerformanceProfile): void;
|
|
268
|
+
/**
|
|
269
|
+
* Gaussian cutoff radius currently in effect, in standard deviations.
|
|
270
|
+
* Resolved at construction, or the last {@link setMaxStdDev} value.
|
|
271
|
+
*/
|
|
272
|
+
get maxStdDev(): number;
|
|
273
|
+
/**
|
|
274
|
+
* Changes the Gaussian cutoff without changing scene data.
|
|
275
|
+
*
|
|
276
|
+
* Lowering it shrinks every quad and is the main fill-rate lever in a busy
|
|
277
|
+
* view; raising it restores the faint outer tail. Rebuilds the material
|
|
278
|
+
* graph, same convention as {@link setPerformanceProfile}.
|
|
279
|
+
*/
|
|
280
|
+
setMaxStdDev(value: number): void;
|
|
281
|
+
/**
|
|
282
|
+
* Copies a decoded scene (or chunk) into the pool and activates it.
|
|
283
|
+
* Ranges are row-aligned in the pool textures, so each allocation may
|
|
284
|
+
* round up to the next multiple of the texture width internally.
|
|
285
|
+
*
|
|
286
|
+
* @returns A handle to pass to {@link removeRange}.
|
|
287
|
+
* @throws {Error} when the remaining pool capacity cannot fit the range.
|
|
288
|
+
*/
|
|
289
|
+
appendRange(data: SplatData): SplatRange;
|
|
290
|
+
/** Appends uploaded data without exposing it to draw/sort until activated. */
|
|
291
|
+
protected appendInactiveRange(data: SplatData): SplatRange;
|
|
292
|
+
/** Reserves an inactive pool range whose data can be filled over multiple frames. */
|
|
293
|
+
protected reserveInactiveRange(count: number): SplatRange;
|
|
294
|
+
/** Writes one contiguous segment of a reserved inactive range. */
|
|
295
|
+
protected writeInactiveRange(handle: SplatRange, data: SplatData, offset: number): void;
|
|
296
|
+
/**
|
|
297
|
+
* Overwrites splats at `offset` within a range **in place** - unlike
|
|
298
|
+
* {@link writeInactiveRange} this allows an *active* range, so the frontier
|
|
299
|
+
* page table can page individual slots of its always-active slab. Queues the
|
|
300
|
+
* touched rows for upload; the caller must {@link invalidateSort} once per
|
|
301
|
+
* batch since splat depths changed.
|
|
302
|
+
*/
|
|
303
|
+
protected overwriteRangeData(handle: SplatRange, data: SplatData, offset: number): void;
|
|
304
|
+
/**
|
|
305
|
+
* Zeros splats `[offset, offset + count)` of a range so they draw nothing
|
|
306
|
+
* (zero covariance → degenerate quad, zero color). Used to free frontier slab
|
|
307
|
+
* slots that leave the frontier. Queues the rows for upload.
|
|
308
|
+
*/
|
|
309
|
+
protected degenerateRange(handle: SplatRange, offset: number, count: number): void;
|
|
310
|
+
/**
|
|
311
|
+
* Copies one chunk's splats into the pool's backing arrays at `destination`.
|
|
312
|
+
* Shared by the append and staged-write paths, which differ only in where
|
|
313
|
+
* the rows come from and when they are activated.
|
|
314
|
+
*/
|
|
315
|
+
private writeSplatRows;
|
|
316
|
+
/**
|
|
317
|
+
* Scatters a chunk's packed SH words into the per-group pool textures.
|
|
318
|
+
*
|
|
319
|
+
* A chunk without SH still writes: pool rows are reused, so leaving the
|
|
320
|
+
* previous occupant's words behind would give a DC-only chunk somebody
|
|
321
|
+
* else's view-dependent color. Zero is not neutral (the range is signed and
|
|
322
|
+
* rarely symmetric), so the code for "0.0" in this scene's range is.
|
|
323
|
+
*/
|
|
324
|
+
private writePackedSh;
|
|
325
|
+
/**
|
|
326
|
+
* Adopts the first packed-SH chunk's dequantization range as the scene's one
|
|
327
|
+
* pool uniform. LCC/`.rad` chunks already share one range; a streamed SOG
|
|
328
|
+
* chunk measures its own extent, so later chunks are requantized into this
|
|
329
|
+
* one at write time ({@link writePackedSh}) rather than ignored.
|
|
330
|
+
*/
|
|
331
|
+
private applyShRange;
|
|
332
|
+
/** The scene's locked packed-SH range as a plain tuple, or null if unset. */
|
|
333
|
+
private currentShRange;
|
|
334
|
+
/** The packed word decoding to 0 in every channel under the scene's range. */
|
|
335
|
+
private neutralShWord;
|
|
336
|
+
/** Warns once per call site when a source's SH cannot be stored. */
|
|
337
|
+
private warnOnIgnoredSh;
|
|
338
|
+
/** Atomically includes or excludes a resident range on the next active-list rebuild. */
|
|
339
|
+
protected setRangeActive(handle: SplatRange, active: boolean): void;
|
|
340
|
+
/**
|
|
341
|
+
* Activates only the first `prefix` splats of a range (see
|
|
342
|
+
* {@link RangeRecord.activePrefix}). The page-table slab keeps its used slots
|
|
343
|
+
* densely packed at the front, so this bounds per-frame sorting and vertex
|
|
344
|
+
* work to the *drawn* frontier instead of the whole pool-sized slab. Write
|
|
345
|
+
* APIs (`overwriteRangeData` / `degenerateRange`) still address the full range.
|
|
346
|
+
*/
|
|
347
|
+
protected setRangeActivePrefix(handle: SplatRange, prefix: number): void;
|
|
348
|
+
/**
|
|
349
|
+
* Replaces the drawn pool indices for an immutable static hierarchy.
|
|
350
|
+
*
|
|
351
|
+
* This is intentionally narrower than the range API: subclasses may select
|
|
352
|
+
* an arbitrary hierarchy frontier, but the referenced pool rows must remain
|
|
353
|
+
* resident for the lifetime of the mesh.
|
|
354
|
+
*/
|
|
355
|
+
protected replaceActiveIndices(indices: Uint32Array): void;
|
|
356
|
+
/** Fast identity-frontier variant that avoids allocating a large index array. */
|
|
357
|
+
protected replaceActivePrefix(count: number): void;
|
|
358
|
+
/** Replaces a static mesh's resident cut without increasing its fixed pool allocation. */
|
|
359
|
+
protected replaceStaticData(data: SplatData): void;
|
|
360
|
+
private appendRangeWithState;
|
|
361
|
+
/**
|
|
362
|
+
* Deactivates a previously appended range and frees its pool rows for
|
|
363
|
+
* reuse. The bounding sphere used for depth quantization stays a
|
|
364
|
+
* conservative superset until new ranges grow it again.
|
|
365
|
+
*/
|
|
366
|
+
removeRange(handle: SplatRange): void;
|
|
367
|
+
/** Maximum number of splats the pool can hold (row-aligned internally). */
|
|
368
|
+
get capacity(): number;
|
|
369
|
+
/** Number of splats currently active (drawn and depth-sorted). */
|
|
370
|
+
get activeSplatCount(): number;
|
|
371
|
+
/**
|
|
372
|
+
* Returns this mesh's current pool state for an internal unified gather.
|
|
373
|
+
* Call {@link update} first for streamed sources so the active list reflects
|
|
374
|
+
* the current LOD cut. Consumers must treat the returned GPU resources as
|
|
375
|
+
* read-only; range lifecycle remains owned by this mesh.
|
|
376
|
+
*/
|
|
377
|
+
getUnifiedSourceView(): UnifiedSourceView;
|
|
378
|
+
/**
|
|
379
|
+
* Marks this mesh as a source drawn by {@link UnifiedSplatRenderer}.
|
|
380
|
+
* The source stays invisible to the regular scene draw, while its own picker
|
|
381
|
+
* mirrors this resolved visibility so existing per-source hit testing works.
|
|
382
|
+
* Internal consumers should clear the state with `null` before disposing.
|
|
383
|
+
*/
|
|
384
|
+
setUnifiedPickVisibility(visible: boolean | null): void;
|
|
385
|
+
/**
|
|
386
|
+
* The visibility that actually decides whether this mesh's splats reach the
|
|
387
|
+
* screen: the owning {@link UnifiedSplatRenderer}'s per-source visibility
|
|
388
|
+
* while one owns the draw, else `Object3D.visible`. Consumers that gate on
|
|
389
|
+
* "is this mesh showing" - the picker, `CameraBudgetGovernor` - must read
|
|
390
|
+
* this rather than `visible`, which a unified renderer forces to `false` on
|
|
391
|
+
* every source it owns purely to keep the regular scene draw from
|
|
392
|
+
* double-drawing them.
|
|
393
|
+
*/
|
|
394
|
+
get effectiveVisibility(): boolean;
|
|
395
|
+
/**
|
|
396
|
+
* Pool splats still allocatable, in whole rows. Because allocation is
|
|
397
|
+
* row-aligned, an append of n splats fits when
|
|
398
|
+
* `ceil(n / rowWidth) · rowWidth ≤ freeSplatCapacity` *and* a contiguous
|
|
399
|
+
* span exists; callers use this for conservative pre-checks before
|
|
400
|
+
* falling back to {@link compact}.
|
|
401
|
+
*/
|
|
402
|
+
get freeSplatCapacity(): number;
|
|
403
|
+
/**
|
|
404
|
+
* Declares a named per-splat data channel - one extra value per pool slot,
|
|
405
|
+
* in its own pool-aligned data texture. Channels are opt-in: nothing is
|
|
406
|
+
* allocated until you call this. Read a channel from a modifier with
|
|
407
|
+
* `ctx.channel(name)` and write values per range with {@link writeChannel}.
|
|
408
|
+
*
|
|
409
|
+
* Because a splat keeps its pool row for its whole residency, a channel
|
|
410
|
+
* value follows the splat across depth re-sorts and pool {@link compact}ion
|
|
411
|
+
* - the persistent per-splat label the SDF/selection effects build on. Call
|
|
412
|
+
* this **before** assigning a modifier that reads the channel; a modifier
|
|
413
|
+
* that reads an undeclared channel is a material-build error.
|
|
414
|
+
*
|
|
415
|
+
* @throws {Error} if a channel of this name already exists.
|
|
416
|
+
*/
|
|
417
|
+
defineChannel(name: string, options?: SplatChannelOptions): void;
|
|
418
|
+
/**
|
|
419
|
+
* Writes channel values for a resident range, at `[offset, offset + data.length)`
|
|
420
|
+
* splats within it (default `offset` 0). Values ride the same staging-upload
|
|
421
|
+
* path as pool data, flushed on the next {@link update}. `byte` channels take
|
|
422
|
+
* raw `0..255`; `float` channels take the value verbatim.
|
|
423
|
+
*
|
|
424
|
+
* @throws {Error} for an unknown channel or range, or a write past the range.
|
|
425
|
+
*/
|
|
426
|
+
writeChannel(range: SplatRange, name: string, data: ArrayLike<number>, offset?: number): void;
|
|
427
|
+
/**
|
|
428
|
+
* Effect hooks, folded into the vertex stage in array order - each
|
|
429
|
+
* modifier sees the running result of the ones before it. Assigning a
|
|
430
|
+
* changed list rebuilds the material (a pipeline recompile); animating a
|
|
431
|
+
* modifier's own uniforms or storage buffers never does. With an empty
|
|
432
|
+
* list the material is exactly the unhooked renderer.
|
|
433
|
+
* See `docs/guide/effects-and-modifiers.md`.
|
|
434
|
+
*/
|
|
435
|
+
get modifiers(): readonly SplatModifier[];
|
|
436
|
+
set modifiers(value: readonly SplatModifier[]);
|
|
437
|
+
/**
|
|
438
|
+
* Core projected-2D depth of field. Adds an isotropic screen-space CoC disc
|
|
439
|
+
* after EWA projection (not the stylized M13 scale modifier). Live uniforms
|
|
440
|
+
* - no material rebuild. Pass `aperture: 0` to disable.
|
|
441
|
+
*/
|
|
442
|
+
setDepthOfField(settings: Partial<DepthOfFieldSettings>): void;
|
|
443
|
+
/**
|
|
444
|
+
* PlayCanvas-style proxy-mesh relighting. Multiplies baked splat color in the
|
|
445
|
+
* **display** fragment by a screen-space sample of `map` (RGB = lit proxy,
|
|
446
|
+
* A = coverage). Pass `null` to disable (`blend → 0`, placeholder map).
|
|
447
|
+
*
|
|
448
|
+
* Blend / brightness / background are live uniforms (no rebuild). Changing
|
|
449
|
+
* the map texture identity rebuilds the material once. Does not affect the
|
|
450
|
+
* pick pass. Does not invalidate a unified gather cache.
|
|
451
|
+
*/
|
|
452
|
+
setRelighting(options: RelightingSettings | null): void;
|
|
453
|
+
/** Current relight numeric uniforms (`blend === 0` means off). */
|
|
454
|
+
getRelighting(): RelightingUniforms;
|
|
455
|
+
/**
|
|
456
|
+
* Moves the screen-radius band (px) without rebuilding the material.
|
|
457
|
+
*
|
|
458
|
+
* Only meaningful on a mesh constructed *with* a band - the graph decides at
|
|
459
|
+
* build time whether to cull on radius at all, from
|
|
460
|
+
* {@link SplatMeshOptions.minSplatScreenRadius} / `maxSplatScreenRadius`.
|
|
461
|
+
*
|
|
462
|
+
* A foveated mesh uses this to keep the band aligned with its LOD cut: the
|
|
463
|
+
* band spans roughly one level, so refining the cut to spend spare budget
|
|
464
|
+
* selects smaller splats, and a band left where it was would cull precisely
|
|
465
|
+
* the detail that refinement bought.
|
|
466
|
+
*/
|
|
467
|
+
protected setScreenRadiusBand(minPx: number, maxPx: number): void;
|
|
468
|
+
/** Current core DoF uniforms (`aperture === 0` means off). */
|
|
469
|
+
getDepthOfField(): DepthOfFieldSettings;
|
|
470
|
+
/**
|
|
471
|
+
* Packs the resident ranges toward the start of the pool, removing the
|
|
472
|
+
* gaps that add/remove churn leaves behind and restoring a single
|
|
473
|
+
* contiguous free span. Use this when {@link appendRange} throws despite
|
|
474
|
+
* enough total free rows (row-alignment fragmentation).
|
|
475
|
+
*
|
|
476
|
+
* The CPU backing arrays are authoritative, so no data is re-fetched;
|
|
477
|
+
* moved rows are re-uploaded to the GPU on the next {@link update}.
|
|
478
|
+
*/
|
|
479
|
+
compact(): void;
|
|
480
|
+
/** {@link SplatPoolTenant}: the ranges this mesh holds in the pool. */
|
|
481
|
+
poolRanges(): Iterable<SplatPoolRange>;
|
|
482
|
+
/**
|
|
483
|
+
* {@link SplatPoolTenant}: follow one range to its new rows. The pool has
|
|
484
|
+
* already moved the splat data; this moves what the mesh keys by pool row.
|
|
485
|
+
*/
|
|
486
|
+
relocatePoolRange(range: SplatPoolRange, targetRow: number): void;
|
|
487
|
+
/** {@link SplatPoolTenant}: rebuild everything keyed by pool index. */
|
|
488
|
+
onPoolCompacted(): void;
|
|
489
|
+
/**
|
|
490
|
+
* Per-frame update: uploads pending pool writes, refreshes the projection
|
|
491
|
+
* uniforms and requests a depth re-sort when the camera or the content
|
|
492
|
+
* has changed. Call this every frame, before `renderer.render`.
|
|
493
|
+
*/
|
|
494
|
+
update(camera: THREE.PerspectiveCamera, renderer: THREE.WebGPURenderer, options?: SplatUpdateOptions): void;
|
|
495
|
+
/** Returns the render-preparation CPU timings for the current update. */
|
|
496
|
+
protected getUpdateTimings(): Readonly<{
|
|
497
|
+
activeListMs: number;
|
|
498
|
+
uploadMs: number;
|
|
499
|
+
sortSubmitMs: number;
|
|
500
|
+
stagingTextureAllocations: number;
|
|
501
|
+
activeListUpdateRanges: number;
|
|
502
|
+
}>;
|
|
503
|
+
/**
|
|
504
|
+
* Skips one otherwise ordinary sort request, but only while the active list
|
|
505
|
+
* is unchanged since the last sort. Streamed staging uses this immediately
|
|
506
|
+
* before a forced commit sort so the GPU queue has a frame to drain; a
|
|
507
|
+
* concurrent swap group that mutates the active list revokes the request,
|
|
508
|
+
* because the depth order must always describe the list being drawn.
|
|
509
|
+
*/
|
|
510
|
+
protected deferNextSortRequest(): void;
|
|
511
|
+
/**
|
|
512
|
+
* Once, on the first update (when the backend is known), fails loudly if the
|
|
513
|
+
* pool's `2048 × poolRows` data textures exceed the device's maximum texture
|
|
514
|
+
* dimension - otherwise the upload fails with a cryptic backend error and a
|
|
515
|
+
* frozen canvas. Streamed scenes stay under the limit (budget-bounded); this
|
|
516
|
+
* guards very large *static* scenes, especially on mobile (≈8192 max → about
|
|
517
|
+
* 16.7 M splats). If the limit can't be read, the check is skipped rather
|
|
518
|
+
* than risk a false rejection.
|
|
519
|
+
*/
|
|
520
|
+
private assertPoolFitsDevice;
|
|
521
|
+
/** Bounding box of the appended splats, in this mesh's local space. */
|
|
522
|
+
computeSplatBounds(): THREE.Box3;
|
|
523
|
+
/**
|
|
524
|
+
* Asynchronously picks the frontmost visible splat under an NDC coordinate.
|
|
525
|
+
*
|
|
526
|
+
* Renders a one-pixel GPU depth pass (shared projection/Gaussian math with
|
|
527
|
+
* the display material), reads the encoded view depth, and reconstructs a
|
|
528
|
+
* world-space point on the splat's rendered center plane. Concurrent calls
|
|
529
|
+
* on this mesh are serialized. Returns `null` for misses, empty meshes,
|
|
530
|
+
* out-of-canvas coordinates, or after {@link dispose}.
|
|
531
|
+
*
|
|
532
|
+
* Does not return a persistent splat id - LOD streaming and pool reuse make
|
|
533
|
+
* identity unstable across frames.
|
|
534
|
+
*/
|
|
535
|
+
pick(ndc: THREE.Vector2, camera: THREE.Camera, renderer: THREE.WebGPURenderer, options?: SplatPickOptions): Promise<SplatPickResult | null>;
|
|
536
|
+
/**
|
|
537
|
+
* The resident splat center nearest a world point, within `radius` (world
|
|
538
|
+
* units), or `null` if none. A synchronous CPU query over the pool's decoded
|
|
539
|
+
* centers - no GPU round-trip - backed by a uniform grid rebuilt only when
|
|
540
|
+
* the resident set changes (M9). The primitive behind measurement markers and
|
|
541
|
+
* proximity tests.
|
|
542
|
+
*
|
|
543
|
+
* **Resident-only:** a {@link StreamedSplatMesh} searches only the splats
|
|
544
|
+
* currently in the pool (the LOD the camera has resolved), so the answer is
|
|
545
|
+
* the nearest *loaded* splat - coarser far from the camera, absent where no
|
|
546
|
+
* chunk has streamed in. A static mesh searches its whole scene.
|
|
547
|
+
*
|
|
548
|
+
* World-correct under any rotation + per-axis (including non-uniform) scale:
|
|
549
|
+
* candidates are gathered in local space with a conservative radius
|
|
550
|
+
* (`radius / min axis scale`) and then ranked by **world** distance, so the
|
|
551
|
+
* nearest-in-world splat wins even when the axes stretch differently. Shear
|
|
552
|
+
* in an ancestor transform is not supported. Splats displaced by GPU
|
|
553
|
+
* modifiers are queried at their undisplaced CPU positions.
|
|
554
|
+
*/
|
|
555
|
+
queryNearest(worldPoint: THREE.Vector3, radius: number): SplatNearestResult | null;
|
|
556
|
+
/**
|
|
557
|
+
* Returns the first resident splat center inside a world-space ray cone.
|
|
558
|
+
* This is a synchronous CPU query, intended for interactions that cannot
|
|
559
|
+
* wait for (or rely on) a GPU readback. `radiusAtUnitDistance` is the cone's
|
|
560
|
+
* world-space radius one unit from the origin; `minimumRadius` keeps nearby
|
|
561
|
+
* point-like splats practical to target.
|
|
562
|
+
*
|
|
563
|
+
* Like {@link queryNearest}, this searches resident, undisplaced CPU centers.
|
|
564
|
+
*/
|
|
565
|
+
queryRay(ray: THREE.Ray, radiusAtUnitDistance?: number, minimumRadius?: number): SplatRayResult | null;
|
|
566
|
+
/**
|
|
567
|
+
* The supporting surface beneath a world point: the highest resident splat
|
|
568
|
+
* that lies no more than `maxDrop` below it (world −Y) and within `radius`
|
|
569
|
+
* horizontally, or `null` if none. A downward probe over splat centers - the
|
|
570
|
+
* primitive behind floor-following and teleport validation - without a GPU
|
|
571
|
+
* pick or a collision mesh (M9), so it works for every format, not only the
|
|
572
|
+
* `.lcc2` captures that ship collision geometry.
|
|
573
|
+
*
|
|
574
|
+
* `radius` (default `maxDrop / 2`) is the horizontal tolerance: splats are
|
|
575
|
+
* points, so an exact vertical hit is unlikely and a small disc is searched.
|
|
576
|
+
* Resident-only semantics and the world-correctness contract (rotation +
|
|
577
|
+
* per-axis scale supported, shear not) are the same as {@link queryNearest};
|
|
578
|
+
* every candidate is judged in world space, where the −Y probe direction is
|
|
579
|
+
* defined, so the vertical test never assumes local axis alignment.
|
|
580
|
+
*/
|
|
581
|
+
queryHeight(worldPoint: THREE.Vector3, maxDrop: number, radius?: number): SplatHeightResult | null;
|
|
582
|
+
/**
|
|
583
|
+
* Renders this mesh from a second camera - into `target`, or the canvas when
|
|
584
|
+
* omitted - with depth order **and** projection correct for *that* camera
|
|
585
|
+
* (M10). The primitive behind mirrors, portals, split panes, and thumbnails.
|
|
586
|
+
*
|
|
587
|
+
* Depth order is view-dependent, but a mesh keeps one sorted order buffer:
|
|
588
|
+
* `update()` sorts it for the single camera it is given, so a second view
|
|
589
|
+
* drawn with that order shows transparency/pop errors. `renderView` re-sorts
|
|
590
|
+
* for `camera` into the shared buffer, sets the view-dependent uniforms
|
|
591
|
+
* (viewport from `target`), draws, and marks the primary order stale so the
|
|
592
|
+
* next `update()` re-sorts for the main camera. GPU submissions execute in
|
|
593
|
+
* order, so each view's draw reads the order and uniforms it just wrote.
|
|
594
|
+
*
|
|
595
|
+
* Call once per extra view per frame, before or after the primary
|
|
596
|
+
* `update()` + render - both are correct. On the WebGL2 fallback the sorter
|
|
597
|
+
* is an asynchronous worker owned by the primary view, so `renderView` does
|
|
598
|
+
* not sort at all there: the secondary view draws with the primary view's
|
|
599
|
+
* order (projection is still per-camera correct) - a documented single-view
|
|
600
|
+
* ordering limitation; WebGPU gets exact per-view order.
|
|
601
|
+
*
|
|
602
|
+
* @param target - Destination render target, or `null`/omitted for the canvas.
|
|
603
|
+
*/
|
|
604
|
+
renderView(camera: THREE.PerspectiveCamera, renderer: THREE.WebGPURenderer, target?: THREE.RenderTarget | null): void;
|
|
605
|
+
/**
|
|
606
|
+
* Sorts the shared order buffer for a secondary view's camera, bypassing the
|
|
607
|
+
* primary view's sort scheduler and its `lastSortedModelView` record (that
|
|
608
|
+
* state belongs to `update()`'s camera). WebGPU dispatches synchronously into
|
|
609
|
+
* the render queue, so the following draw reads this order.
|
|
610
|
+
*/
|
|
611
|
+
private sortForView;
|
|
612
|
+
/** Rebuilds the query grid if the resident set changed since it was built. */
|
|
613
|
+
private ensureQueryGrid;
|
|
614
|
+
/** World-space position of pool splat `poolIndex`, written into `out`. */
|
|
615
|
+
private splatWorldPosition;
|
|
616
|
+
/**
|
|
617
|
+
* The smallest world axis scale, for conservatively mapping a world radius
|
|
618
|
+
* into local space: a world sphere of radius `r` fits inside a local sphere
|
|
619
|
+
* of `r / minAxisScale`, for any per-axis (non-uniform) scale.
|
|
620
|
+
*/
|
|
621
|
+
private queryWorldMinScale;
|
|
622
|
+
/**
|
|
623
|
+
* Releases every GPU resource and worker this mesh owns. Idempotent: a
|
|
624
|
+
* second call is a no-op, and a render loop that outlives the mesh by a
|
|
625
|
+
* frame is safe - {@link update}, {@link renderView} and {@link pick} all
|
|
626
|
+
* become no-ops after dispose.
|
|
627
|
+
*/
|
|
628
|
+
dispose(): void;
|
|
629
|
+
/** Matches the viewport / focal / local-camera uniforms written by {@link update}. */
|
|
630
|
+
private refreshProjectionUniforms;
|
|
631
|
+
/**
|
|
632
|
+
* Writes the three view-dependent uniforms - viewport, focal (splat screen
|
|
633
|
+
* size), and the camera position in mesh-local space (SH view direction +
|
|
634
|
+
* screen-radius math) - for a camera drawing into a viewport of the given
|
|
635
|
+
* pixel size. {@link update} passes the canvas size (or, while an XR session
|
|
636
|
+
* presents, the per-eye viewport); {@link renderView} passes its render
|
|
637
|
+
* target's size.
|
|
638
|
+
*
|
|
639
|
+
* @param positionCamera - Camera whose world position seeds the SH view
|
|
640
|
+
* direction and screen-radius math. Defaults to `camera`; in XR it is the
|
|
641
|
+
* head, so both eyes shade from one cyclopean point while each still
|
|
642
|
+
* projects through its own eye.
|
|
643
|
+
*/
|
|
644
|
+
private writeViewUniforms;
|
|
645
|
+
/**
|
|
646
|
+
* Self-adjusts {@link foveationLimitPx} so the frontier cut's drawn-splat count
|
|
647
|
+
* tracks {@link foveationDrawBudget} (Spark's `maxSplats` feedback). Estimates
|
|
648
|
+
* the drawn count by replaying the cut on a strided sample of the pool, then
|
|
649
|
+
* coarsens (grows the limit) when over budget and refines (shrinks toward the
|
|
650
|
+
* finest {@link foveationTargetPx}) when comfortably under. Throttled; a no-op
|
|
651
|
+
* outside `'frontier'` mode.
|
|
652
|
+
*
|
|
653
|
+
* `viewportY` must be the **same** height {@link writeViewUniforms} is given
|
|
654
|
+
* for this frame - the two share `foveationLimitPx` in pixels and each
|
|
655
|
+
* converts it with its own `focalY`, so a mismatch (the full drawing buffer
|
|
656
|
+
* here against a per-eye viewport there) makes the estimator judge a cut the
|
|
657
|
+
* shader is not applying, and the ratchet runs away toward the maximum.
|
|
658
|
+
*/
|
|
659
|
+
private adaptFoveationLimit;
|
|
660
|
+
/** Records a written row span for GPU upload and the sort-worker mirror. */
|
|
661
|
+
private markRowsWritten;
|
|
662
|
+
/** Appends one newly active pool range to the packed source-index list. */
|
|
663
|
+
private activateRecord;
|
|
664
|
+
/**
|
|
665
|
+
* Removes one active range and backfills its holes from the packed tail.
|
|
666
|
+
* The common contiguous case uses one native block copy; the fallback
|
|
667
|
+
* handles a range split by earlier tail backfills in O(record.count).
|
|
668
|
+
*/
|
|
669
|
+
private deactivateRecord;
|
|
670
|
+
/** Uploads only the changed packed slots and invalidates the current depth order. */
|
|
671
|
+
private commitActiveListMutation;
|
|
672
|
+
/** Rewrites the active-splat list (pool indices, range by range). */
|
|
673
|
+
private rebuildActiveList;
|
|
674
|
+
/** The pool's reusable index ramp; shared by every mesh drawing from it. */
|
|
675
|
+
private getPoolIndexTemplate;
|
|
676
|
+
/**
|
|
677
|
+
* Uploads rows written since the last flush by copying only those rows
|
|
678
|
+
* through a staging texture. (Constructor-time writes never appear here;
|
|
679
|
+
* they ride the textures' initial full upload instead.)
|
|
680
|
+
*/
|
|
681
|
+
private flushPendingUploads;
|
|
682
|
+
/**
|
|
683
|
+
* Packs float32 CPU backing into the half GPU images for centers/covA.
|
|
684
|
+
* Used once after a static constructor write so the initial `needsUpdate`
|
|
685
|
+
* upload carries half bits (staging is skipped for that path).
|
|
686
|
+
*/
|
|
687
|
+
private syncHalfFloatPoolImages;
|
|
688
|
+
/**
|
|
689
|
+
* Uploads the given row spans of one or more same-layout pool textures via
|
|
690
|
+
* per-region staging copies. Spans are merged first so consecutive appends
|
|
691
|
+
* upload as one rectangle each - the copyTextureToTexture call count, not
|
|
692
|
+
* the pixel volume, dominates.
|
|
693
|
+
*/
|
|
694
|
+
private uploadRows;
|
|
695
|
+
/** Reuses a same-length half encode buffer for one staging key. */
|
|
696
|
+
private acquireHalfEncodeBuffer;
|
|
697
|
+
/**
|
|
698
|
+
* Reuses one of the recent same-sized staging textures for this upload. WebGPU
|
|
699
|
+
* texture dimensions are immutable, so each cached entry remains exact-sized. Keeping
|
|
700
|
+
* a small LRU per channel avoids recreating all core/SH staging resources when
|
|
701
|
+
* a stream alternates among a few chunk heights, while bounding GPU memory.
|
|
702
|
+
*/
|
|
703
|
+
private acquireUploadStaging;
|
|
704
|
+
/** Gathers everything the material graph reads. See `splat-mesh-material.ts`. */
|
|
705
|
+
private graphInputs;
|
|
706
|
+
private buildMaterial;
|
|
707
|
+
private requestSortIfNeeded;
|
|
708
|
+
/**
|
|
709
|
+
* Refreshes {@link boundingSphereLocal} - the sphere the sorter quantizes
|
|
710
|
+
* depth over - when {@link boundsDirty}. The base mesh uses its local splat
|
|
711
|
+
* bounds; a unified {@link SplatScene} overrides this to supply a world-space
|
|
712
|
+
* bound spanning all its sources (whose transforms live in the shader).
|
|
713
|
+
*/
|
|
714
|
+
protected refreshSortBounds(): void;
|
|
715
|
+
/** The data texture backing a channel defined with {@link defineChannel}. */
|
|
716
|
+
protected channelTexture(name: string): THREE.DataTexture | undefined;
|
|
717
|
+
/** CPU backing storage of a float channel, for internal CPU-side consumers. */
|
|
718
|
+
protected channelBacking(name: string): Float32Array;
|
|
719
|
+
/**
|
|
720
|
+
* Forces the next {@link update} to re-sort even if the camera has not moved
|
|
721
|
+
* - used by a unified pool when a source's transform changes, since the depth
|
|
722
|
+
* order then changes without any camera motion.
|
|
723
|
+
*/
|
|
724
|
+
protected invalidateSort(): void;
|
|
725
|
+
private createSorter;
|
|
726
|
+
/** Prefetches the experimental radix sorter; safe to call repeatedly. */
|
|
727
|
+
private ensureRadixSorter;
|
|
728
|
+
}
|
|
729
|
+
/** Default frontier-cut target size (px). Matches Spark's `lodRenderScale`
|
|
730
|
+
* default of 1 - nodes refine until ~1 px on screen, so the draw budget (not a
|
|
731
|
+
* coarser fixed cut) is what bounds detail. At the old value of 4 the cut
|
|
732
|
+
* stopped ~2 LOD levels early everywhere the tree doesn't bottom out at
|
|
733
|
+
* leaves, which made large-scale scenes visibly coarser than Spark. */
|
|
734
|
+
export declare const DEFAULT_FOVEATION_TARGET_PX = 1;
|
|
735
|
+
/** Default target for the frontier cut's drawn-splat count (Spark's `maxSplats`).
|
|
736
|
+
* Coarsens the cut once the estimate exceeds it, keeping frame cost bounded. */
|
|
737
|
+
export declare const DEFAULT_FOVEATION_DRAW_BUDGET = 900000;
|