@voluma/vlam 0.1.0 → 0.2.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.
- package/LICENSE +21 -0
- package/README.md +60 -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,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The TSL material graph behind `SplatMesh`.
|
|
3
|
+
*
|
|
4
|
+
* One graph serves both of the mesh's materials: `display` writes premultiplied
|
|
5
|
+
* color, `pick` encodes linear view depth. They must stay one builder - a pick
|
|
6
|
+
* that disagreed with the display about projection, covariance or visibility
|
|
7
|
+
* would return hits for splats the viewer cannot see.
|
|
8
|
+
*
|
|
9
|
+
* Everything here runs once, at material build time, and reads only what the
|
|
10
|
+
* mesh hands it, so these are free functions rather than methods. Note the two
|
|
11
|
+
* inputs that must be passed live rather than copied: the uniform *node
|
|
12
|
+
* instances* (display and pick share them, so a frame updates both at once) and
|
|
13
|
+
* the channels map (a rebuild after `defineChannel` has to see the new entry).
|
|
14
|
+
*
|
|
15
|
+
* Internal. Nothing here is exported from `index.ts`.
|
|
16
|
+
*/
|
|
17
|
+
import * as THREE from 'three/webgpu';
|
|
18
|
+
import { uniformArray } from 'three/tsl';
|
|
19
|
+
import type { SplatModifier } from './splat-modifier';
|
|
20
|
+
import { foldSplatModifierStack } from './splat-modifier-stack';
|
|
21
|
+
import type { SplatPerformanceProfile } from './splat-mesh';
|
|
22
|
+
/** Default variance scale for isotropic point mode: (0.35 × min-axis)². */
|
|
23
|
+
export declare const DEFAULT_ISOTROPIC_VARIANCE_SCALE: number;
|
|
24
|
+
/** Default isotropic screen-space sigma radius (px); only used when a modifier opts in. */
|
|
25
|
+
export declare const DEFAULT_ISOTROPIC_SCREEN_RADIUS_PX = 1;
|
|
26
|
+
/**
|
|
27
|
+
* Blends `Σ` toward σ²·I where σ² = λ_min(Σ) · varianceScale. Spark point
|
|
28
|
+
* mode sets every scale axis to min(scale)·0.35; since λ_min(Σ) = min(scale)²,
|
|
29
|
+
* that is exactly σ² = (min·0.35)².
|
|
30
|
+
*
|
|
31
|
+
* λ_min is estimated without `Σ⁻¹` (near-singular on flat Gaussians, which
|
|
32
|
+
* made n·Σ·n blow up into large blobs): take the min of the three axis
|
|
33
|
+
* Rayleigh quotients and the Rayleigh along the longest row-cross of Σ (a
|
|
34
|
+
* stable thin-axis hint when Σ is rank-deficient). The `normal` argument is
|
|
35
|
+
* kept for call-site compatibility and ignored.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyIsotropicCovarianceOverride(covariance: THREE.Node<'mat3'>, _normal: THREE.Node<'vec3'>, mixFactor: THREE.Node<'float'>, varianceScale: THREE.Node<'float'>): THREE.Node<'mat3'>;
|
|
38
|
+
/** Sets both screen-space eigenvalues to min(λ1, λ2) for a circular footprint. */
|
|
39
|
+
export declare function equalizeProjectedEigenvalues(lambda1: THREE.Node<'float'>, lambda2: THREE.Node<'float'>, mixFactor: THREE.Node<'float'>): {
|
|
40
|
+
lambda1: THREE.Node<'float'>;
|
|
41
|
+
lambda2: THREE.Node<'float'>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Caps isotropic λ to a screen-space sigma radius while blending by mix.
|
|
45
|
+
* `screenRadiusPx ≤ 0` is a no-op (opt-in only; 0 must not shrink λ toward zero).
|
|
46
|
+
*/
|
|
47
|
+
export declare function capProjectedEigenvaluesToScreenRadius(lambda1: THREE.Node<'float'>, lambda2: THREE.Node<'float'>, mixFactor: THREE.Node<'float'>, screenRadiusPx: THREE.Node<'float'>, maxStdDev: THREE.Node<'float'>): {
|
|
48
|
+
lambda1: THREE.Node<'float'>;
|
|
49
|
+
lambda2: THREE.Node<'float'>;
|
|
50
|
+
};
|
|
51
|
+
/** The pool data textures a graph samples per splat. */
|
|
52
|
+
export interface SplatMaterialTextures {
|
|
53
|
+
centersTexture: THREE.DataTexture;
|
|
54
|
+
colorsTexture: THREE.DataTexture;
|
|
55
|
+
covarianceATexture: THREE.DataTexture;
|
|
56
|
+
covarianceBTexture: THREE.DataTexture;
|
|
57
|
+
}
|
|
58
|
+
/** Narrow a TSL expression to the typed {@link THREE.Node} our hook contract
|
|
59
|
+
* expects. Identity at runtime - satisfies TypeScript only. */
|
|
60
|
+
export declare function asNode<T extends string>(node: unknown): THREE.Node<T>;
|
|
61
|
+
/** A `vec3` uniform, named so its type can be referred to in field decls. */
|
|
62
|
+
export declare function vec3Uniform(): THREE.UniformNode<"vec3", THREE.Vector3>;
|
|
63
|
+
export type Vec3Uniform = ReturnType<typeof vec3Uniform>;
|
|
64
|
+
declare function vec2Uniform(): THREE.UniformNode<"vec2", THREE.Vector2>;
|
|
65
|
+
/** A `vec2` uniform (focal, viewport). */
|
|
66
|
+
export type Vec2Uniform = ReturnType<typeof vec2Uniform>;
|
|
67
|
+
declare function floatUniform(): THREE.UniformNode<"float", number>;
|
|
68
|
+
/** A scalar uniform (pick thresholds and planes). */
|
|
69
|
+
export type FloatUniform = ReturnType<typeof floatUniform>;
|
|
70
|
+
/**
|
|
71
|
+
* How the material reads a splat's higher-order SH coefficients. The two
|
|
72
|
+
* sources differ only in where a coefficient comes from - the band
|
|
73
|
+
* accumulation and view-direction math are shared:
|
|
74
|
+
*
|
|
75
|
+
* - `palette`: SOG/`.lcc2` shN. Coefficients live in a per-file codebook and
|
|
76
|
+
* each splat stores a label; only a static mesh can use it, because two
|
|
77
|
+
* files' palettes cannot be merged into one pool.
|
|
78
|
+
* - `packed`: LCC `Quality`, `.rad`, etc. Each splat carries its own coefficients as packed
|
|
79
|
+
* words in pool-shaped textures, so appended ranges keep their SH.
|
|
80
|
+
*/
|
|
81
|
+
export type SplatShInputs = {
|
|
82
|
+
mode: 'palette';
|
|
83
|
+
bands: number;
|
|
84
|
+
paletteTexture: THREE.DataTexture;
|
|
85
|
+
} | {
|
|
86
|
+
mode: 'packed';
|
|
87
|
+
bands: 1 | 2 | 3;
|
|
88
|
+
textures: readonly THREE.DataTexture[];
|
|
89
|
+
range: {
|
|
90
|
+
min: Vec3Uniform;
|
|
91
|
+
max: Vec3Uniform;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Per-source placement inputs for a unified pool ({@link SplatScene}): the
|
|
96
|
+
* splat's source id plus the shared array of source matrices. Drives two things
|
|
97
|
+
* at once - the splat's mesh-local position (`M · poolCenter`, applied before
|
|
98
|
+
* the modifier stack) and the frame view-dependent SH is evaluated in.
|
|
99
|
+
*/
|
|
100
|
+
export interface SplatSourcePlacement {
|
|
101
|
+
/** Pool-aligned source id channel. */
|
|
102
|
+
sourceIdTexture: THREE.DataTexture;
|
|
103
|
+
/** Four column vectors per source world matrix. */
|
|
104
|
+
columns: ReturnType<typeof uniformArray>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The `mat4 · (poolCenter, 1)` placed center for source id `s`, plus the linear
|
|
108
|
+
* 3×3 part as a `mat3`, read from the shared column array (three.js
|
|
109
|
+
* `Matrix4.elements` are column-major, so a column maps to
|
|
110
|
+
* `elements[k*4 .. k*4+3]`).
|
|
111
|
+
*
|
|
112
|
+
* Lives here rather than in `source-transform.ts` because the material graph and
|
|
113
|
+
* the sorter must place a splat identically. Keeping it on the material side
|
|
114
|
+
* also avoids a runtime dependency from `source-transform.ts` back into the
|
|
115
|
+
* material graph.
|
|
116
|
+
*/
|
|
117
|
+
export declare function sourceWorldTransform(columns: ReturnType<typeof uniformArray>, sourceId: THREE.Node<'int'>, localCenter: THREE.Node<'vec3'>): {
|
|
118
|
+
worldCenter: THREE.Node<'vec3'>;
|
|
119
|
+
linear: THREE.Node<'mat3'>;
|
|
120
|
+
};
|
|
121
|
+
/** Coefficients per channel for a band count (0 → none, 3 → 3rd order). */
|
|
122
|
+
export declare function shCoefficientCount(bands: number): number;
|
|
123
|
+
/**
|
|
124
|
+
* Builds the per-coefficient accessor for whichever SH source the mesh has -
|
|
125
|
+
* the only part of the SH graph that differs between them.
|
|
126
|
+
*
|
|
127
|
+
* `palette` indirects through the splat's codebook label; `packed` reads the
|
|
128
|
+
* splat's own words straight out of the pool-shaped integer textures and
|
|
129
|
+
* unpacks them (R: bits 0-10, G: 11-20, B: 21-31, each a unit fraction of its
|
|
130
|
+
* field, dequantized across the scene's range). The packed texel loads are
|
|
131
|
+
* hoisted into variables so all 15 coefficients cost at most four fetches.
|
|
132
|
+
*/
|
|
133
|
+
export declare function shCoefficientReader(sh: SplatShInputs, textures: {
|
|
134
|
+
covarianceBTexture: THREE.DataTexture;
|
|
135
|
+
}, splatTexel: THREE.Node<'ivec2'>): (c: number) => THREE.Node<'vec3'>;
|
|
136
|
+
/** Evaluates a source's higher-order SH contribution for a local view direction. */
|
|
137
|
+
export declare function evaluateSplatSh(sh: SplatShInputs, textures: {
|
|
138
|
+
covarianceBTexture: THREE.DataTexture;
|
|
139
|
+
}, splatTexel: THREE.Node<'ivec2'>, direction: THREE.Node<'vec3'>): THREE.Node<'vec3'>;
|
|
140
|
+
/** Everything the material graph reads, gathered by the mesh. */
|
|
141
|
+
export interface SplatMaterialBuildInputs {
|
|
142
|
+
textures: SplatMaterialTextures;
|
|
143
|
+
sh: SplatShInputs | null;
|
|
144
|
+
/**
|
|
145
|
+
* Per-source placement for a unified pool, or `null` for a plain mesh. When
|
|
146
|
+
* present, a splat's mesh-local center is `M · poolCenter` and SH is
|
|
147
|
+
* evaluated in the source's own frame.
|
|
148
|
+
*/
|
|
149
|
+
sourcePlacement: SplatSourcePlacement | null;
|
|
150
|
+
/**
|
|
151
|
+
* The mesh's uniform *node instances*, not their values: display and pick
|
|
152
|
+
* share them, so a per-frame write reaches both graphs.
|
|
153
|
+
*/
|
|
154
|
+
uniforms: {
|
|
155
|
+
focal: Vec2Uniform;
|
|
156
|
+
viewport: Vec2Uniform;
|
|
157
|
+
localCameraPosition: Vec3Uniform;
|
|
158
|
+
/** Frontier-cut limit on `own_size / distance` (`foveationMode: 'frontier'`). */
|
|
159
|
+
pixelScaleLimit: FloatUniform;
|
|
160
|
+
/**
|
|
161
|
+
* Core projected-2D DoF focus plane (world/view units). Live uniform -
|
|
162
|
+
* racking focus does not rebuild the material. See `depth-of-field.ts`.
|
|
163
|
+
*/
|
|
164
|
+
dofFocusDistance: FloatUniform;
|
|
165
|
+
/** Core DoF aperture; `0` disables. Live uniform. */
|
|
166
|
+
dofAperture: FloatUniform;
|
|
167
|
+
/**
|
|
168
|
+
* Screen-radius band bounds in px, live so a foveated mesh can follow its
|
|
169
|
+
* own LOD cut. Whether the band exists at all is still decided at build
|
|
170
|
+
* time from `settings.minScreenRadiusPx` / `maxScreenRadiusPx`; only the
|
|
171
|
+
* bounds move.
|
|
172
|
+
*
|
|
173
|
+
* They have to move: the band spans one LOD level, so a mesh that refines
|
|
174
|
+
* its cut to spend spare budget would have exactly that new detail culled
|
|
175
|
+
* for being smaller than a bound chosen for the coarser cut.
|
|
176
|
+
*/
|
|
177
|
+
screenBandMin: FloatUniform;
|
|
178
|
+
screenBandMax: FloatUniform;
|
|
179
|
+
/**
|
|
180
|
+
* Proxy-mesh relight map (RGB = lit, A = coverage). Live texture binding -
|
|
181
|
+
* swap via the TextureNode / rebuild when the mesh updates `setRelighting`.
|
|
182
|
+
* Display fragment only; pick ignores this.
|
|
183
|
+
*/
|
|
184
|
+
relightMap: THREE.Texture;
|
|
185
|
+
/** `0` = baked color only; `1` = full modulate. Live uniform. */
|
|
186
|
+
relightBlend: FloatUniform;
|
|
187
|
+
/** Scales lit RGB (PlayCanvas default ~2 for 0.5 gray proxy). Live. */
|
|
188
|
+
relightBrightness: FloatUniform;
|
|
189
|
+
/** Multiplier where coverage alpha is 0 (sky). Live. */
|
|
190
|
+
relightBackground: FloatUniform;
|
|
191
|
+
/**
|
|
192
|
+
* Coverage soft edge in screen pixels. Live; `0` = hard mask.
|
|
193
|
+
* See {@link RelightingSettings.softness}.
|
|
194
|
+
*/
|
|
195
|
+
relightSoftness: FloatUniform;
|
|
196
|
+
};
|
|
197
|
+
/** Pick-only uniforms. Required when building in `'pick'` mode. */
|
|
198
|
+
pick: {
|
|
199
|
+
alphaThreshold: FloatUniform;
|
|
200
|
+
near: FloatUniform;
|
|
201
|
+
far: FloatUniform;
|
|
202
|
+
} | null;
|
|
203
|
+
/** Baked at build time; changing any of these needs a rebuild. */
|
|
204
|
+
settings: {
|
|
205
|
+
maxStdDev: number;
|
|
206
|
+
/** Screen-space floor on each quad axis, px (0/undefined = off). */
|
|
207
|
+
minSplatSizePx?: number;
|
|
208
|
+
antialias: boolean;
|
|
209
|
+
/** Classic LCC uses XGRIDS' smaller, always-compensated low-pass. */
|
|
210
|
+
projectedFilterProfile: 'default' | 'lcc';
|
|
211
|
+
srgbOutput: boolean;
|
|
212
|
+
performanceProfile: SplatPerformanceProfile;
|
|
213
|
+
/** Cull splats whose projected radius exceeds this many px (0 = off). */
|
|
214
|
+
maxScreenRadiusPx?: number;
|
|
215
|
+
/** Foveation band lower bound: cull splats *below* this many px (0 = off). */
|
|
216
|
+
minScreenRadiusPx?: number;
|
|
217
|
+
/**
|
|
218
|
+
* `.rad` foveation cut. `'band'` (default/undefined) uses the screen-radius
|
|
219
|
+
* band above; `'frontier'` uses Spark's exact per-splat tree cut driven by
|
|
220
|
+
* `own_size` (from the covariance) and the `parent_size` packed in
|
|
221
|
+
* `covarianceB.w`. See `docs/formats/rad-notes.md` M14.6.
|
|
222
|
+
*/
|
|
223
|
+
foveationMode?: 'band' | 'frontier' | 'pagetable';
|
|
224
|
+
/**
|
|
225
|
+
* Cap on a rendered splat's major/minor axis ratio (0/undefined = off). Tames
|
|
226
|
+
* far-field needle/spike artifacts from very anisotropic Gaussians and
|
|
227
|
+
* expansion-enlarged coarse LOD nodes. Baked into the material graph.
|
|
228
|
+
*/
|
|
229
|
+
maxAspect?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Spark's LOD alpha encoding (`.rad`): the stored opacity byte is `alpha/2`,
|
|
232
|
+
* so the shader multiplies by 2 to recover `alpha ∈ [0,2]`; `alpha > 1` marks
|
|
233
|
+
* a merged node whose σ-cutoff grows (`+0.7·(remap−1)`) and whose falloff
|
|
234
|
+
* becomes a super-Gaussian, covering its subtree without scaling covariance.
|
|
235
|
+
*/
|
|
236
|
+
lodAlpha?: boolean;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* The mesh's live channel map, not a copy: `defineChannel` adds to it and
|
|
240
|
+
* then rebuilds, and the graph must resolve names against the new entry.
|
|
241
|
+
*/
|
|
242
|
+
channels: ReadonlyMap<string, {
|
|
243
|
+
texture: THREE.DataTexture;
|
|
244
|
+
}>;
|
|
245
|
+
modifiers: readonly SplatModifier[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Shared TSL graph for display and pick materials: projection, covariance,
|
|
249
|
+
* modifiers, visibility, and Gaussian falloff. Display writes premultiplied
|
|
250
|
+
* color; pick encodes linear view depth into RGB with alpha as the hit flag.
|
|
251
|
+
*
|
|
252
|
+
* @param inputs.pick - Required when `mode` is `'pick'`.
|
|
253
|
+
*/
|
|
254
|
+
export declare function applySplatMaterialGraph(material: THREE.NodeMaterial, mode: 'display' | 'pick', inputs: SplatMaterialBuildInputs): void;
|
|
255
|
+
export { foldSplatModifierStack };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPU screen-space picking for `SplatMesh`.
|
|
3
|
+
*
|
|
4
|
+
* Picking renders the splats once more into a 1×1 target, with a
|
|
5
|
+
* material that writes linear view depth instead of color; the depth comes back
|
|
6
|
+
* through an async readback and unprojects to a world point. That needs a whole
|
|
7
|
+
* subsystem of its own - a render target, a proxy mesh, a private scene, saved
|
|
8
|
+
* renderer state - none of which the mesh itself ever touches, so it lives here
|
|
9
|
+
* rather than as ten more fields on an already large class.
|
|
10
|
+
*
|
|
11
|
+
* The picker reaches back into the mesh through {@link SplatPickHost} rather
|
|
12
|
+
* than holding the mesh itself: the four things it needs are otherwise private,
|
|
13
|
+
* and widening them for the picker would put them in the published .d.ts.
|
|
14
|
+
*
|
|
15
|
+
* Internal. Nothing here is exported from `index.ts`.
|
|
16
|
+
*/
|
|
17
|
+
import * as THREE from 'three/webgpu';
|
|
18
|
+
import type { SplatPickOptions, SplatPickResult } from './splat-mesh';
|
|
19
|
+
import type { FloatUniform } from './splat-mesh-material';
|
|
20
|
+
/** What the picker needs from the mesh it picks. */
|
|
21
|
+
export interface SplatPickHost {
|
|
22
|
+
/** The mesh, for the transform/visibility the proxy mirrors and its geometry. */
|
|
23
|
+
readonly mesh: THREE.Mesh;
|
|
24
|
+
isDisposed(): boolean;
|
|
25
|
+
getActiveCount(): number;
|
|
26
|
+
/** The mesh's live viewport uniform value, in drawing-buffer pixels. */
|
|
27
|
+
getViewportSize(): THREE.Vector2;
|
|
28
|
+
/** Visibility as resolved by an owning unified renderer, when any. */
|
|
29
|
+
getPickVisible(): boolean;
|
|
30
|
+
/** Whether a sorter exists. A pick never creates one - see {@link prepare}. */
|
|
31
|
+
hasSorter(): boolean;
|
|
32
|
+
updateWorldMatrix(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Brings the GPU to the state `update()` would leave it in: flush pending
|
|
35
|
+
* uploads, refresh the projection uniforms, and refresh an existing sorter's
|
|
36
|
+
* draw list. Ordering matters, so the mesh owns it.
|
|
37
|
+
*/
|
|
38
|
+
prepare(camera: THREE.Camera, renderer: THREE.WebGPURenderer): void;
|
|
39
|
+
/**
|
|
40
|
+
* Writes focal / viewport / local-camera uniforms for a camera drawing into
|
|
41
|
+
* a viewport of `width`×`height` pixels. The pick pass crops the frustum to
|
|
42
|
+
* one source pixel and renders into a 1×1 target, so it must rewrite these
|
|
43
|
+
* after the crop and restore the canvas-sized values before returning.
|
|
44
|
+
*/
|
|
45
|
+
setView(camera: THREE.Camera, width: number, height: number): void;
|
|
46
|
+
/** Builds the pick-mode TSL graph onto a freshly created material. */
|
|
47
|
+
applyPickGraph(material: THREE.NodeMaterial): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Owns the pick pass and everything it allocates. Resources are created on the
|
|
51
|
+
* first pick, so a mesh that is never picked pays nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare class SplatPicker {
|
|
54
|
+
private readonly host;
|
|
55
|
+
/** Alpha below which a splat is transparent enough to pick through. */
|
|
56
|
+
private readonly alphaThreshold;
|
|
57
|
+
/** Camera planes for the pick material's depth encoding. */
|
|
58
|
+
private readonly near;
|
|
59
|
+
private readonly far;
|
|
60
|
+
private material;
|
|
61
|
+
private proxy;
|
|
62
|
+
private target;
|
|
63
|
+
/** Reused sub-frustum camera; preserves the caller's concrete camera type. */
|
|
64
|
+
private pickCamera;
|
|
65
|
+
private readonly scene;
|
|
66
|
+
/** Serializes picks: they share one render target and one renderer. */
|
|
67
|
+
private queue;
|
|
68
|
+
private readonly point;
|
|
69
|
+
private readonly savedClearColor;
|
|
70
|
+
constructor(host: SplatPickHost);
|
|
71
|
+
/** The pick uniforms the material graph binds. */
|
|
72
|
+
get uniforms(): {
|
|
73
|
+
alphaThreshold: FloatUniform;
|
|
74
|
+
near: FloatUniform;
|
|
75
|
+
far: FloatUniform;
|
|
76
|
+
};
|
|
77
|
+
pick(ndc: THREE.Vector2, camera: THREE.Camera, renderer: THREE.WebGPURenderer, options?: SplatPickOptions): Promise<SplatPickResult | null>;
|
|
78
|
+
/** Rebuilds the pick graph after a settings change. No-op before the first pick. */
|
|
79
|
+
rebuildMaterial(): void;
|
|
80
|
+
/** Flags the pick graph for recompile (e.g. the modifier list changed). */
|
|
81
|
+
markNeedsUpdate(): void;
|
|
82
|
+
dispose(): void;
|
|
83
|
+
private run;
|
|
84
|
+
private ensureResources;
|
|
85
|
+
/** Maps one original framebuffer pixel onto the complete 1×1 pick target. */
|
|
86
|
+
private cropCameraToPixel;
|
|
87
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Texture-pool primitives behind `SplatMesh`.
|
|
3
|
+
*
|
|
4
|
+
* Splat attributes live in pool data textures, a fixed number of texels wide,
|
|
5
|
+
* and every range the mesh hands out is a whole number of rows in them. This
|
|
6
|
+
* module holds the parts of that scheme with no dependency on the mesh's own
|
|
7
|
+
* state: creating the textures, measuring the backend's limits, and the row-span
|
|
8
|
+
* bookkeeping the allocator runs on.
|
|
9
|
+
*
|
|
10
|
+
* {@link SplatPool} is public - a host builds one to let several meshes draw
|
|
11
|
+
* from a shared memory envelope. The rest is internal.
|
|
12
|
+
*/
|
|
13
|
+
import * as THREE from 'three/webgpu';
|
|
14
|
+
/**
|
|
15
|
+
* Texels per row in every pool data texture.
|
|
16
|
+
*
|
|
17
|
+
* A range is row-aligned, so this also sets the allocation granularity: a
|
|
18
|
+
* 10-splat chunk still occupies a whole 2048-texel row. Wider rows waste more
|
|
19
|
+
* on small chunks; narrower rows need more rows than a backend may allow.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SPLAT_DATA_TEXTURE_WIDTH = 2048;
|
|
22
|
+
/** A free span of rows in the pool, as a row index and a row count. */
|
|
23
|
+
export interface RowSpan {
|
|
24
|
+
start: number;
|
|
25
|
+
count: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Takes `rowCount` contiguous rows out of `spans`, best-fit.
|
|
29
|
+
*
|
|
30
|
+
* Best-fit rather than first-fit: a chunk pool churns ranges of very different
|
|
31
|
+
* sizes, and spending the smallest span that fits keeps the large ones intact
|
|
32
|
+
* for the large chunks that have nowhere else to go.
|
|
33
|
+
*
|
|
34
|
+
* Mutates `spans` in place. Throws when nothing contiguous is left, which is
|
|
35
|
+
* the caller's cue to compact.
|
|
36
|
+
*
|
|
37
|
+
* @param poolRows - Total rows in the pool; named in the failure only.
|
|
38
|
+
* @returns The starting row of the allocated span.
|
|
39
|
+
*/
|
|
40
|
+
export declare function allocateRowSpan(spans: RowSpan[], rowCount: number, poolRows: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Returns a span to the free list, coalescing it with any neighbours.
|
|
43
|
+
*
|
|
44
|
+
* Merging on release is what keeps {@link allocateRowSpan} able to satisfy a
|
|
45
|
+
* large request after many small ranges have come and gone; without it the
|
|
46
|
+
* free list fragments into unusable slivers.
|
|
47
|
+
*
|
|
48
|
+
* @returns The new free list, sorted by start row.
|
|
49
|
+
*/
|
|
50
|
+
export declare function releaseRowSpan(spans: RowSpan[], start: number, count: number): RowSpan[];
|
|
51
|
+
/** CPU-side mirror of the pool textures, kept for partial uploads. */
|
|
52
|
+
export interface SplatPoolBacking {
|
|
53
|
+
centers: Float32Array;
|
|
54
|
+
colors: Uint8Array;
|
|
55
|
+
covarianceA: Float32Array;
|
|
56
|
+
covarianceB: Float32Array;
|
|
57
|
+
/** Per-splat packed SH; texture t holds coefficients 4t..4t+3. */
|
|
58
|
+
shPacked: Uint32Array[];
|
|
59
|
+
}
|
|
60
|
+
/** A tenant's allocation in the pool, as a row index and a row count. */
|
|
61
|
+
export interface SplatPoolRange {
|
|
62
|
+
startRow: number;
|
|
63
|
+
rowCount: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A mesh that draws from a {@link SplatPool}.
|
|
67
|
+
*
|
|
68
|
+
* Only compaction needs this: packing the pool relocates rows belonging to
|
|
69
|
+
* every tenant, so each must be able to enumerate its ranges, follow them to
|
|
70
|
+
* their new rows, and rebuild whatever it keyed by pool index.
|
|
71
|
+
*/
|
|
72
|
+
export interface SplatPoolTenant {
|
|
73
|
+
/** The tenant's current allocations. */
|
|
74
|
+
poolRanges(): Iterable<SplatPoolRange>;
|
|
75
|
+
/**
|
|
76
|
+
* Adopts a new start row for one of this tenant's ranges. The pool has
|
|
77
|
+
* already moved the splat data; the tenant moves anything else keyed by pool
|
|
78
|
+
* row (per-splat channels) and records that those rows need re-uploading.
|
|
79
|
+
*/
|
|
80
|
+
relocatePoolRange(range: SplatPoolRange, targetRow: number): void;
|
|
81
|
+
/** Called once after a compaction, so the tenant can rebuild draw state. */
|
|
82
|
+
onPoolCompacted(): void;
|
|
83
|
+
}
|
|
84
|
+
/** Options for {@link SplatPool}. */
|
|
85
|
+
export interface SplatPoolOptions {
|
|
86
|
+
/** Splats the pool must hold; rounded up to whole rows. */
|
|
87
|
+
capacity: number;
|
|
88
|
+
/**
|
|
89
|
+
* Texture precision for centers and covarianceA. `'float16'` halves their
|
|
90
|
+
* VRAM; covarianceB stays float32 either way because it packs integer IDs
|
|
91
|
+
* (SOG palette label, `.rad` frontier parent) that halves cannot represent
|
|
92
|
+
* exactly above 2048.
|
|
93
|
+
*/
|
|
94
|
+
floatTextures?: 'float32' | 'float16';
|
|
95
|
+
/** Bands of per-splat (non-palette) SH to allocate for; 0 disables. */
|
|
96
|
+
packedShBands?: 0 | 1 | 2 | 3;
|
|
97
|
+
/** Packed-SH coefficient count for `packedShBands`, supplied by the caller
|
|
98
|
+
* so this module stays free of the SH packing tables. */
|
|
99
|
+
packedShTextureCount?: number;
|
|
100
|
+
/**
|
|
101
|
+
* The device's `maxTextureDimension2D`, so a pool too tall for it fails here
|
|
102
|
+
* rather than at first draw. Pass {@link deviceMaxTextureSize}.
|
|
103
|
+
*
|
|
104
|
+
* `SplatMesh.update` also checks this, but only once and only if it is ever
|
|
105
|
+
* reached: a pool created past the limit and drawn through a path that
|
|
106
|
+
* swallows that first throw renders forever against invalid textures, which
|
|
107
|
+
* surfaces as an unreadable cascade of WebGPU "invalid due to a previous
|
|
108
|
+
* error" validation failures and a black canvas. Checking at construction is
|
|
109
|
+
* both earlier (before the allocation) and unskippable.
|
|
110
|
+
*
|
|
111
|
+
* Omitted or 0 skips the check, so a caller that cannot read the limit is not
|
|
112
|
+
* falsely rejected.
|
|
113
|
+
*/
|
|
114
|
+
maxTextureSize?: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The splat storage a mesh draws from: the data textures, their CPU backing,
|
|
118
|
+
* and the row allocator that hands out space in them.
|
|
119
|
+
*
|
|
120
|
+
* Split out of `SplatMesh` because storage and drawing have different
|
|
121
|
+
* lifetimes and, ultimately, different owners. A mesh's *draw identity* is
|
|
122
|
+
* already independent of where its splats sit - `splatIndex` maps each
|
|
123
|
+
* instance to a pool index and the sorter rewrites it freely - so the pool
|
|
124
|
+
* behind those indices is a separate concern, and one pool can in principle
|
|
125
|
+
* back several meshes (each keeping its own active list of pool indices).
|
|
126
|
+
*
|
|
127
|
+
* Allocation is row-aligned and best-fit; see {@link allocateRowSpan}.
|
|
128
|
+
*/
|
|
129
|
+
export declare class SplatPool {
|
|
130
|
+
readonly width = 2048;
|
|
131
|
+
readonly rows: number;
|
|
132
|
+
readonly floatTextures: 'float32' | 'float16';
|
|
133
|
+
readonly packedShBands: 0 | 1 | 2 | 3;
|
|
134
|
+
readonly backing: SplatPoolBacking;
|
|
135
|
+
readonly centersTexture: THREE.DataTexture;
|
|
136
|
+
readonly colorsTexture: THREE.DataTexture;
|
|
137
|
+
readonly covarianceATexture: THREE.DataTexture;
|
|
138
|
+
readonly covarianceBTexture: THREE.DataTexture;
|
|
139
|
+
readonly shPackedTextures: readonly THREE.DataTexture[];
|
|
140
|
+
/** Free row spans, sorted by start row. Mutated in place by the allocator. */
|
|
141
|
+
freeRowSpans: RowSpan[];
|
|
142
|
+
private readonly tenants;
|
|
143
|
+
/**
|
|
144
|
+
* Pool index → its owner's packed active-list slot, valid only while that
|
|
145
|
+
* pool index is active.
|
|
146
|
+
*
|
|
147
|
+
* Pool-owned rather than per-mesh because it is keyed by *pool* index: a
|
|
148
|
+
* per-mesh copy would have to span the whole pool, so N meshes sharing one
|
|
149
|
+
* pool would each pay 4 B per pool splat (a 4 M-splat pool shared by 13
|
|
150
|
+
* meshes = ~200 MB of reverse maps). One array is safe because a row belongs
|
|
151
|
+
* to exactly one tenant at a time, so no two tenants ever write the same
|
|
152
|
+
* entry - the slot numbers in it are simply read back by whoever owns
|
|
153
|
+
* the row.
|
|
154
|
+
*/
|
|
155
|
+
readonly activeSlotByPoolIndex: Uint32Array;
|
|
156
|
+
private poolIndexTemplate;
|
|
157
|
+
constructor(options: SplatPoolOptions);
|
|
158
|
+
/**
|
|
159
|
+
* A reusable `0, 1, 2, …` ramp over the pool, used to fill runs of pool
|
|
160
|
+
* indices and active-list slots without a per-element loop. Pool-owned so
|
|
161
|
+
* tenants sharing a pool share the one copy.
|
|
162
|
+
*/
|
|
163
|
+
indexTemplate(): Uint32Array;
|
|
164
|
+
/** Splats the pool holds, always a whole number of rows. */
|
|
165
|
+
get capacity(): number;
|
|
166
|
+
/** Whether `texture` belongs to this pool (and so outlives any one tenant). */
|
|
167
|
+
isPoolTexture(texture: THREE.Texture): boolean;
|
|
168
|
+
/** The four core textures, in the order the upload path indexes them. */
|
|
169
|
+
get coreTextures(): readonly THREE.DataTexture[];
|
|
170
|
+
/** Rows still free, whether or not they are contiguous. */
|
|
171
|
+
get freeRows(): number;
|
|
172
|
+
/** Takes `rowCount` contiguous rows; throws when none are left (compact cue). */
|
|
173
|
+
allocateRows(rowCount: number): number;
|
|
174
|
+
/** Returns rows to the free list, coalescing neighbours. */
|
|
175
|
+
releaseRows(start: number, count: number): void;
|
|
176
|
+
/** Resets the free list to one span covering the whole pool. */
|
|
177
|
+
resetFreeRows(fromRow?: number): void;
|
|
178
|
+
/**
|
|
179
|
+
* Adds a mesh that draws from this pool. Registration exists for
|
|
180
|
+
* {@link compact}: packing the pool moves rows that belong to *every*
|
|
181
|
+
* tenant, so each has to be told where its ranges went.
|
|
182
|
+
*/
|
|
183
|
+
register(tenant: SplatPoolTenant): void;
|
|
184
|
+
/**
|
|
185
|
+
* Drops a tenant. Release its ranges first: an unregistered tenant is
|
|
186
|
+
* invisible to {@link compact}, which would then treat rows it still holds
|
|
187
|
+
* as free and hand them to someone else. `compact` throws rather than let
|
|
188
|
+
* that happen silently.
|
|
189
|
+
*/
|
|
190
|
+
unregister(tenant: SplatPoolTenant): void;
|
|
191
|
+
/** Number of meshes drawing from this pool. */
|
|
192
|
+
get tenantCount(): number;
|
|
193
|
+
/**
|
|
194
|
+
* Packs every tenant's ranges toward row 0, removing the gaps that add /
|
|
195
|
+
* remove churn leaves behind and restoring a single contiguous free span.
|
|
196
|
+
* Callers reach this when {@link allocateRows} throws despite enough total
|
|
197
|
+
* free rows - row-alignment fragmentation.
|
|
198
|
+
*
|
|
199
|
+
* The CPU backing arrays are authoritative, so nothing is re-fetched; moved
|
|
200
|
+
* rows are re-uploaded on the tenant's next update.
|
|
201
|
+
*
|
|
202
|
+
* Note this is a whole-pool stall: with several tenants, one mesh's
|
|
203
|
+
* fragmentation relocates the others' rows too, and every tenant rebuilds
|
|
204
|
+
* its draw state. (Spark sidesteps the equivalent by allocating fixed-size,
|
|
205
|
+
* interchangeable pages that never need packing; vlam's ranges are variable
|
|
206
|
+
* runs, so it packs instead.)
|
|
207
|
+
*/
|
|
208
|
+
compact(): void;
|
|
209
|
+
dispose(): void;
|
|
210
|
+
}
|
|
211
|
+
export declare function createDataTexture(data: Float32Array | Uint8Array | Uint16Array, width: number, height: number, type: THREE.TextureDataType): THREE.DataTexture;
|
|
212
|
+
/**
|
|
213
|
+
* An RGBA32UI data texture: four raw `uint32` per texel, delivered to the
|
|
214
|
+
* shader as a `uvec4` with no filtering or normalization. Packed SH words go
|
|
215
|
+
* through this rather than a float texture so they survive bit-exact.
|
|
216
|
+
*/
|
|
217
|
+
export declare function createIntegerDataTexture(data: Uint32Array, width: number, height: number): THREE.DataTexture;
|
|
218
|
+
/**
|
|
219
|
+
* The renderer backend's maximum 2D texture dimension, or 0 when it can't be
|
|
220
|
+
* read (in which case callers skip any limit check rather than falsely reject).
|
|
221
|
+
* WebGPU exposes `device.limits.maxTextureDimension2D`; WebGL2 exposes
|
|
222
|
+
* `gl.MAX_TEXTURE_SIZE`.
|
|
223
|
+
*/
|
|
224
|
+
export declare function deviceMaxTextureSize(renderer: THREE.WebGPURenderer): number;
|
|
225
|
+
/**
|
|
226
|
+
* Throws when a pool's data textures would be taller than the device allows.
|
|
227
|
+
*
|
|
228
|
+
* `maxTextureSize <= 0` means "limit unknown" and skips the check rather than
|
|
229
|
+
* risk a false rejection; see {@link deviceMaxTextureSize}.
|
|
230
|
+
*
|
|
231
|
+
* @param rows - Rows the pool needs.
|
|
232
|
+
* @param maxTextureSize - The device's `maxTextureDimension2D`.
|
|
233
|
+
* @param width - Texels per row, for the splat count in the message.
|
|
234
|
+
*/
|
|
235
|
+
export declare function assertPoolRowsFitDevice(rows: number, maxTextureSize: number, width?: number): void;
|
|
236
|
+
/**
|
|
237
|
+
* Three's WebGPU backend submits one `queue.writeBuffer` per update range.
|
|
238
|
+
* Merge touching mutations here so a region-atomic commit does not turn a
|
|
239
|
+
* handful of contiguous backfills into several main-thread submissions.
|
|
240
|
+
*/
|
|
241
|
+
export declare function addMergedUpdateRange(attribute: THREE.BufferAttribute, start: number, count: number): void;
|