@voluma/vlam 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +3 -2
  2. package/dist/budget-governor.d.ts +4 -4
  3. package/dist/camera-budget-governor.d.ts +11 -11
  4. package/dist/chunk-cache-budget.d.ts +5 -5
  5. package/dist/chunk-fetch-scheduler.d.ts +4 -4
  6. package/dist/chunk-loader-DlNwggfG.js +1859 -0
  7. package/dist/chunk-loader-DlNwggfG.js.map +1 -0
  8. package/dist/chunk-loader.d.ts +2 -2
  9. package/dist/compute-sorter.d.ts +1 -1
  10. package/dist/effects.d.ts +11 -10
  11. package/dist/effects.js.map +1 -1
  12. package/dist/formats/ksplat/index.d.ts +1 -1
  13. package/dist/formats/lcc/collision-mesh.d.ts +2 -2
  14. package/dist/formats/lcc.js +17 -16
  15. package/dist/formats/lcc.js.map +1 -1
  16. package/dist/formats/ply/index.d.ts +1 -1
  17. package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
  18. package/dist/formats/ply.js +1 -1
  19. package/dist/formats/ply.js.map +1 -1
  20. package/dist/formats/rad/index.d.ts +1 -1
  21. package/dist/formats/rad/parse-rad.d.ts +1 -1
  22. package/dist/formats/rad/rad.d.ts +2 -2
  23. package/dist/formats/rad.js +3 -3
  24. package/dist/formats/rad.js.map +1 -1
  25. package/dist/formats/sog/index.d.ts +1 -1
  26. package/dist/formats/sog/parse-sog.d.ts +4 -4
  27. package/dist/formats/sog.js.map +1 -1
  28. package/dist/formats/splat/index.d.ts +1 -1
  29. package/dist/formats/spz/index.d.ts +1 -1
  30. package/dist/index.d.ts +16 -25
  31. package/dist/index.js +253 -9047
  32. package/dist/index.js.map +1 -1
  33. package/dist/{load-scene.d.ts → load-splat-data.d.ts} +15 -13
  34. package/dist/loaders.d.ts +15 -0
  35. package/dist/loaders.js +26 -0
  36. package/dist/loaders.js.map +1 -0
  37. package/dist/loading-wLo8vRbA.js.map +1 -1
  38. package/dist/loading.d.ts +8 -8
  39. package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
  40. package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
  41. package/dist/lod-scheduler.d.ts +1 -1
  42. package/dist/lod-source.d.ts +5 -5
  43. package/dist/{splat-scene.d.ts → merged-splat-mesh.d.ts} +14 -15
  44. package/dist/orientation.d.ts +7 -7
  45. package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
  46. package/dist/{ply-header-BwgExCn-.js.map → ply-header-B9WllbPg.js.map} +1 -1
  47. package/dist/radix-sorter-R0FCSqKu.js +178 -0
  48. package/dist/radix-sorter-R0FCSqKu.js.map +1 -0
  49. package/dist/relighting-CSQF3UcL.js +969 -0
  50. package/dist/relighting-CSQF3UcL.js.map +1 -0
  51. package/dist/relighting.d.ts +1 -1
  52. package/dist/selection.d.ts +13 -0
  53. package/dist/selection.js +137 -0
  54. package/dist/selection.js.map +1 -0
  55. package/dist/source-transform.d.ts +4 -4
  56. package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
  57. package/dist/{splat-budget-DXew_86c.js.map → splat-budget-DuVZspPQ.js.map} +1 -1
  58. package/dist/splat-budget.d.ts +1 -1
  59. package/dist/splat-data-BZM5dhhx.js.map +1 -1
  60. package/dist/splat-data.d.ts +4 -4
  61. package/dist/splat-mesh-aQdcWA6v.js +1942 -0
  62. package/dist/splat-mesh-aQdcWA6v.js.map +1 -0
  63. package/dist/splat-mesh-material.d.ts +2 -2
  64. package/dist/splat-mesh-types-BZIko-_9.js +18 -0
  65. package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
  66. package/dist/splat-mesh-types.d.ts +14 -8
  67. package/dist/splat-mesh.d.ts +4 -4
  68. package/dist/splat-modifier.d.ts +5 -5
  69. package/dist/splat-partition.d.ts +1 -1
  70. package/dist/static-lod-entry.d.ts +12 -0
  71. package/dist/static-lod-splat-mesh.d.ts +2 -2
  72. package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
  73. package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
  74. package/dist/static-lod.js +148 -0
  75. package/dist/static-lod.js.map +1 -0
  76. package/dist/streamed-splat-mesh.d.ts +21 -27
  77. package/dist/streaming.d.ts +23 -0
  78. package/dist/streaming.js +2877 -0
  79. package/dist/streaming.js.map +1 -0
  80. package/dist/{unified-splat-renderer.d.ts → unified-splat-mesh.d.ts} +10 -10
  81. package/dist/unified-work-buffer.d.ts +66 -0
  82. package/dist/unified.d.ts +11 -0
  83. package/dist/unified.js +738 -0
  84. package/dist/unified.js.map +1 -0
  85. package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
  86. package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
  87. package/dist/webgpu-limits.d.ts +0 -59
  88. package/dist/work-buffer-gather.d.ts +1 -1
  89. package/package.json +24 -4
  90. package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
@@ -0,0 +1,738 @@
1
+ var je = Object.defineProperty;
2
+ var Oe = (r, n, e) => n in r ? je(r, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[n] = e;
3
+ var s = (r, n, e) => Oe(r, typeof n != "symbol" ? n + "" : n, e);
4
+ import * as l from "three/webgpu";
5
+ import { uniform as w, storage as C, Fn as we, If as ze, float as R, instanceIndex as ce, int as ve, ivec2 as We, textureLoad as ee, mat3 as Ae, vec3 as D, vec4 as M, bool as Ie, colorSpaceToWorking as Ne, varying as oe, positionGeometry as ge, modelViewMatrix as Ce, cameraProjectionMatrix as _e, mix as ae, vec2 as z, Discard as qe, texture as te, screenUV as re } from "three/tsl";
6
+ import { S as ke, o as He, p as Ke, q as ne, s as Xe, t as Ye, d as Ze, u as $e, w as Qe, m as Je, b as et, D as tt, c as rt, W as st, C as it, i as ot, j as at, y as nt, r as ct } from "./relighting-CSQF3UcL.js";
7
+ import { RadixSorter as lt } from "./radix-sorter-R0FCSqKu.js";
8
+ import { i as ut } from "./splat-budget-DuVZspPQ.js";
9
+ import { a as dt } from "./webgpu-limits-Y3CI_6_6.js";
10
+ const ue = class ue {
11
+ constructor(n) {
12
+ /** World-space center xyz; w is the drawable flag (1 draw, 0 modifier-hidden). */
13
+ s(this, "centers");
14
+ /** Resolved color and opacity per work slot. */
15
+ s(this, "colors");
16
+ /** First packed covariance row per work slot. */
17
+ s(this, "covarianceA");
18
+ /** Second packed covariance row per work slot. */
19
+ s(this, "covarianceB");
20
+ /** Per-slot isotropic covariance mix (0 = keep Σ, 1 = round dot). */
21
+ s(this, "isotropicMix");
22
+ /** Per-slot isotropic target sigma radius in screen px (0 = disabled). */
23
+ s(this, "isotropicScreenRadius");
24
+ s(this, "mirrors");
25
+ this.capacity = n;
26
+ const e = n + ue.SCRATCH_SLOTS;
27
+ this.centers = new l.StorageBufferAttribute(new Float32Array(e * 4), 4), this.colors = new l.StorageBufferAttribute(new Float32Array(e * 4), 4), this.covarianceA = new l.StorageBufferAttribute(new Float32Array(e * 4), 4), this.covarianceB = new l.StorageBufferAttribute(new Float32Array(e * 4), 4), this.isotropicMix = new l.StorageBufferAttribute(new Float32Array(e), 1), this.isotropicScreenRadius = new l.StorageBufferAttribute(new Float32Array(e), 1), this.mirrors = new ke([
28
+ this.centers,
29
+ this.colors,
30
+ this.covarianceA,
31
+ this.covarianceB,
32
+ this.isotropicMix,
33
+ this.isotropicScreenRadius
34
+ ]);
35
+ }
36
+ /**
37
+ * Drops the JS-heap copies three keeps behind these storage buffers.
38
+ *
39
+ * Every one of them is written only by the gather compute pass and read only
40
+ * by the draw material - the CPU side is zeroes that no code ever reads, and
41
+ * three's WebGPU backend never frees it on its own. At 72 B/slot that mirror
42
+ * is ~720 MB of JS heap on a 10M-slot scene. See `storage-attribute-mirror`.
43
+ *
44
+ * Safe to call after any dispatch that bound this buffer; a no-op once
45
+ * settled, and on WebGL2 or a renderer double that has no WebGPU backend.
46
+ */
47
+ releaseCpuMirrors(n) {
48
+ this.mirrors.settled || this.mirrors.release(n);
49
+ }
50
+ };
51
+ /**
52
+ * Slots past {@link capacity}, reserved so a pipeline warm-up has somewhere
53
+ * harmless to write. See {@link WorkBufferGather.warmUp}: compiling a compute
54
+ * pipeline requires actually dispatching it, and dispatching zero workgroups
55
+ * - while it does compile - makes WebGPU log an "unusual workgroup count of 0"
56
+ * warning for every source. One workgroup aimed at this tail compiles just the
57
+ * same, silently, and cannot disturb a drawn slot: admission never allocates
58
+ * past `capacity` and the draw's `instanceCount` never covers it.
59
+ */
60
+ s(ue, "SCRATCH_SLOTS", 256);
61
+ let le = ue;
62
+ class ht {
63
+ constructor(n) {
64
+ /** World-space `xyzw` center buffer, one element per unified work slot. */
65
+ s(this, "centers");
66
+ /** Resolved source RGBA values; production draw consumes this next. */
67
+ s(this, "colors");
68
+ /** Packed symmetric world-space covariance rows. */
69
+ s(this, "covarianceA");
70
+ s(this, "covarianceB");
71
+ s(this, "isotropicMix");
72
+ s(this, "isotropicScreenRadius");
73
+ /** Shared target for this source's gathered range. */
74
+ s(this, "workBuffer");
75
+ s(this, "capacity");
76
+ s(this, "activeCount", w(0));
77
+ s(this, "targetOffset", w(0));
78
+ s(this, "sourceMatrix", w(new l.Matrix4()));
79
+ s(this, "cameraViewMatrix", w(new l.Matrix4()));
80
+ s(this, "opacity", w(1));
81
+ s(this, "pass");
82
+ const {
83
+ capacity: e,
84
+ sourceCapacity: o,
85
+ sourceIndex: a,
86
+ centersTexture: c,
87
+ colorsTexture: g,
88
+ covarianceATexture: b,
89
+ covarianceBTexture: u,
90
+ dataTextureWidth: p
91
+ } = n, T = n.sh ?? null, V = n.modifiers ?? [], P = n.channels ?? /* @__PURE__ */ new Map(), m = n.localCameraPosition ?? w(new l.Vector3());
92
+ this.workBuffer = n.workBuffer ?? new le(e), this.capacity = this.workBuffer.capacity, this.centers = this.workBuffer.centers, this.colors = this.workBuffer.colors, this.covarianceA = this.workBuffer.covarianceA, this.covarianceB = this.workBuffer.covarianceB, this.isotropicMix = this.workBuffer.isotropicMix, this.isotropicScreenRadius = this.workBuffer.isotropicScreenRadius;
93
+ const v = C(a, "uint", o), A = C(this.centers, "vec4", e), k = C(this.colors, "vec4", e), t = C(this.covarianceA, "vec4", e), i = C(this.covarianceB, "vec4", e), d = C(this.isotropicMix, "float", e), B = C(this.isotropicScreenRadius, "float", e);
94
+ this.pass = we(() => {
95
+ ze(R(ce).lessThan(this.activeCount), () => {
96
+ const y = ve(v.element(ce)), f = We(
97
+ y.mod(ve(p)),
98
+ y.div(ve(p))
99
+ ), S = ee(c, f).xyz, x = ce.add(this.targetOffset.toUint()), F = ee(b, f), U = ee(u, f), L = Ae(
100
+ D(F.x, F.y, F.z),
101
+ D(F.y, F.w, U.x),
102
+ D(F.z, U.x, U.y)
103
+ ), E = ee(g, f), W = T === null ? E : M(
104
+ E.rgb.add(
105
+ He(
106
+ T,
107
+ { covarianceBTexture: u },
108
+ f,
109
+ S.sub(m).normalize()
110
+ )
111
+ ).clamp(0, 1),
112
+ E.a
113
+ ), j = this.sourceMatrix.mul(M(S, 1)).xyz, h = Ke(V, m, {
114
+ index: y,
115
+ localCenter: S,
116
+ color: W,
117
+ makeWorldCenter: () => j,
118
+ makeViewCenter: () => this.cameraViewMatrix.mul(M(j, 1)).xyz,
119
+ makeNormal: () => {
120
+ const G = L.inverse(), H = m.sub(S), ie = G.mul(G.mul(H)).normalize();
121
+ return ie.mul(ie.dot(H).sign());
122
+ },
123
+ makeChannel: (G) => {
124
+ const H = P.get(G);
125
+ if (!H)
126
+ throw new Error(
127
+ `UnifiedSplatMesh: source modifier reads undefined channel "${G}".`
128
+ );
129
+ return ee(H.texture, f).r;
130
+ }
131
+ }), I = h.offset === null ? S : S.add(h.offset), N = h.visible === null ? Ie(!0) : h.visible, Z = this.sourceMatrix.mul(M(I, 1)).xyz;
132
+ A.element(x).assign(M(Z, N.select(R(1), R(0))));
133
+ const $ = n.srgbOutput ? h.color : ne(Ne(h.color, l.SRGBColorSpace)), de = n.lodAlpha ? ne($.a.mul(2)) : ne($.a);
134
+ k.element(x).assign(M($.rgb, N.select(de.mul(this.opacity), R(0))));
135
+ const se = h.rotation === null ? L : h.rotation.mul(L).mul(h.rotation.transpose());
136
+ let K = ne(se);
137
+ if (h.isotropicCovarianceMix !== null) {
138
+ const G = h.isotropicVarianceScale ?? R(Xe);
139
+ K = Ye(
140
+ K,
141
+ D(0, 0, 1),
142
+ h.isotropicCovarianceMix,
143
+ G
144
+ );
145
+ }
146
+ const _ = this.sourceMatrix.toMat3(), X = _.mul(K).mul(_.transpose()), O = h.scaleSquared === null ? X : X.mul(h.scaleSquared), q = O.mul(D(1, 0, 0)), Q = O.mul(D(0, 1, 0)), he = O.mul(D(0, 0, 1));
147
+ t.element(x).assign(M(q.x, q.y, q.z, Q.y)), i.element(x).assign(M(Q.z, he.z, 0, 0)), d.element(x).assign(h.isotropicCovarianceMix === null ? R(0) : h.isotropicCovarianceMix), B.element(x).assign(
148
+ h.isotropicCovarianceMix === null ? R(0) : h.isotropicScreenRadiusPx ?? R(0)
149
+ );
150
+ });
151
+ })().compute(o, [256]);
152
+ }
153
+ /** Gathers a source's active pool slots into a contiguous work-buffer range. */
154
+ gather(n, e, o, a, c = new l.Matrix4(), g = 1) {
155
+ if (e < 0 || o < 0 || o + e > this.capacity)
156
+ throw new Error("WorkBufferGather: requested range exceeds work-buffer capacity.");
157
+ this.activeCount.value = e, this.targetOffset.value = o, this.sourceMatrix.value.copy(a);
158
+ const b = typeof c == "number" ? new l.Matrix4() : c;
159
+ this.cameraViewMatrix.value.copy(b), this.opacity.value = Math.max(
160
+ 0,
161
+ typeof c == "number" ? c : g
162
+ ), this.pass.count = e, n.compute(this.pass), this.workBuffer.releaseCpuMirrors(n);
163
+ }
164
+ /**
165
+ * Compiles this gather's compute pipeline without gathering anything.
166
+ *
167
+ * WebGPU compiles a compute pipeline lazily, at its first dispatch - not when
168
+ * the node graph is built. Measured on a 13-mesh scene, a source's *first*
169
+ * `gather` took up to 2.0s while every later dispatch of the same pipeline
170
+ * took under 0.4ms, and because `gather` runs inside `update` that compile
171
+ * landed whole in one render frame. Running it through the async path here
172
+ * compiles off the critical frame, so the first real gather finds it ready.
173
+ *
174
+ * The dispatch targets {@link WorkBuffer.SCRATCH_SLOTS} past the drawable
175
+ * capacity rather than dispatching zero workgroups: zero also compiles, but
176
+ * WebGPU warns about it once per source, and writing into a drawn slot could
177
+ * leave junk behind when a cached gather is reused instead of re-run.
178
+ *
179
+ * Best-effort: a failure here costs only the stall it was meant to avoid, so
180
+ * callers may ignore it.
181
+ */
182
+ async warmUp(n) {
183
+ this.activeCount.value = 1, this.targetOffset.value = this.capacity, this.pass.count = 1, await n.computeAsync(this.pass), this.activeCount.value = 0, this.targetOffset.value = 0, this.workBuffer.releaseCpuMirrors(n);
184
+ }
185
+ /** Releases the gather pipeline. The owner releases the storage attribute. */
186
+ dispose() {
187
+ this.pass.dispose();
188
+ }
189
+ }
190
+ function Pe(r) {
191
+ const n = new l.NodeMaterial(), e = C(r.centers, "vec4", r.capacity), o = C(r.order, "float", r.capacity), a = C(r.colors, "vec4", r.capacity), c = C(r.covarianceA, "vec4", r.capacity), g = C(r.covarianceB, "vec4", r.capacity), b = C(r.isotropicMix, "float", r.capacity), u = C(r.isotropicScreenRadius, "float", r.capacity), p = oe(M(1, 1, 1, 1), "vWorkColor"), T = oe(ge.xy, "vWorkQuadPosition"), V = oe(R(1), "vWorkOpacityCompensation"), P = oe(R(0), "vWorkAdjustedStdDev");
192
+ return n.vertexNode = we(() => {
193
+ const m = o.element(ce).toInt(), v = e.element(m), A = v.xyz, k = v.w.greaterThan(0.5);
194
+ p.assign(a.element(m));
195
+ const t = Ce.mul(M(A, 1)).toVar(), i = _e.mul(t).toVar(), d = c.element(m), B = g.element(m), y = Ae(
196
+ D(d.x, d.y, d.z),
197
+ D(d.y, d.w, B.x),
198
+ D(d.z, B.x, B.y)
199
+ ), f = R(1).div(t.z), S = f.mul(f), x = D(
200
+ r.focal.x.mul(f),
201
+ 0,
202
+ r.focal.x.negate().mul(t.x).mul(S)
203
+ ), F = D(
204
+ 0,
205
+ r.focal.y.mul(f),
206
+ r.focal.y.negate().mul(t.y).mul(S)
207
+ ), U = Ce.toMat3().transpose(), L = U.mul(x), E = U.mul(F), W = L.dot(y.mul(L)), j = E.dot(y.mul(E)), h = L.dot(y.mul(E)), I = t.z.negate().max(1e-4), N = r.dofFocusDistance.max(1e-4), Z = r.dofAperture.max(0).mul(0.5).div(N).atan(), $ = I.sub(N).abs().div(I), de = r.focal.x.mul(Z.tan()), se = $.mul(de), K = se.mul(se).min(R(Ze)), _ = W.add(r.projectedLowPassVariance).add(K), X = j.add(r.projectedLowPassVariance).add(K), O = h, q = b.element(m), Q = _.mul(X).sub(O.mul(O)).max(1e-9), he = W.mul(j).sub(h.mul(h)).max(0), G = W.add(r.projectedLowPassVariance).mul(j.add(r.projectedLowPassVariance)).sub(h.mul(h)).max(1e-9), H = he.div(Q).sqrt(), ie = G.div(Q).sqrt(), De = r.antialias.max(r.compensateProjectedLowPass), Te = ae(ie, H, De);
208
+ V.assign(ae(Te, R(1), q));
209
+ const xe = _.add(X).mul(0.5), ye = z(_.sub(X).mul(0.5), O).length();
210
+ let Y = xe.add(ye), J = xe.sub(ye).max(0);
211
+ const be = $e(Y, J, q);
212
+ Y = be.lambda1, J = be.lambda2;
213
+ const Be = Qe(
214
+ Y,
215
+ J,
216
+ q,
217
+ u.element(m),
218
+ r.maxStdDev
219
+ );
220
+ Y = Be.lambda1, J = Be.lambda2;
221
+ const Fe = p.a.mul(4).sub(3).min(5), fe = p.a.greaterThan(1).select(r.maxStdDev.add(Fe.sub(1).mul(0.7)), r.maxStdDev);
222
+ P.assign(fe);
223
+ const pe = z(O, Y.sub(_)).add(z(1e-6, 0)).normalize(), Me = r.minSplatSizePx, Le = pe.mul(Y.sqrt().mul(fe).min(1024).max(Me)), Ue = z(pe.y, pe.x.negate()).mul(
224
+ J.sqrt().mul(fe).min(1024).max(Me)
225
+ ), Ee = Le.mul(ge.x).add(Ue.mul(ge.y)), Ve = i.xy.div(i.w), Ge = M(
226
+ Ve.add(Ee.mul(2).div(r.viewport)),
227
+ i.z.div(i.w),
228
+ 1
229
+ ), me = i.w.mul(1.2);
230
+ return i.z.greaterThan(me.negate()).and(i.x.abs().lessThan(me)).and(i.y.abs().lessThan(me)).and(k).select(Ge, M(0, 0, 2, 1));
231
+ })(), n.fragmentNode = we(() => {
232
+ const m = T.dot(T);
233
+ qe(m.greaterThan(1));
234
+ const v = m.mul(P.mul(P).mul(-0.5)).exp(), A = p.a.mul(4).sub(3).min(5), k = A.mul(A).sub(1).mul(1 / Math.E).exp(), t = v.oneMinus().pow(k).oneMinus(), d = p.a.greaterThan(1).select(t, v.mul(p.a)).mul(V), B = te(r.relightMap, re), y = r.relightSoftness.div(r.viewport.x.max(1)), f = r.relightSoftness.div(r.viewport.y.max(1)), S = B, x = te(r.relightMap, re.add(z(y, 0))), F = te(r.relightMap, re.add(z(y.negate(), 0))), U = te(r.relightMap, re.add(z(0, f))), L = te(r.relightMap, re.add(z(0, f.negate()))), E = S.a.add(x.a).add(F.a).add(U.a).add(L.a).max(1e-4), W = S.rgb.mul(S.a).add(x.rgb.mul(x.a)).add(F.rgb.mul(F.a)).add(U.rgb.mul(U.a)).add(L.rgb.mul(L.a)).div(E), j = E.mul(0.2), h = M(W, j), I = r.relightSoftness.greaterThan(0.5).select(h, B), N = ae(
235
+ D(r.relightBackground),
236
+ I.rgb.mul(r.relightBrightness),
237
+ I.a
238
+ ), Z = ae(p.rgb, p.rgb.mul(N), r.relightBlend);
239
+ return M(Z.mul(d), d);
240
+ })(), n.transparent = !0, n.depthTest = !0, n.depthWrite = !1, n.side = l.DoubleSide, n.blending = l.CustomBlending, n.blendSrc = l.OneFactor, n.blendDst = l.OneMinusSrcAlphaFactor, n.blendSrcAlpha = l.OneFactor, n.blendDstAlpha = l.OneMinusSrcAlphaFactor, n.toneMapped = !1, n;
241
+ }
242
+ const ft = 16, pt = 72;
243
+ function Re(r) {
244
+ if (!Number.isFinite(r) || r < 0)
245
+ throw new RangeError("Splat capacity must be a non-negative finite number.");
246
+ }
247
+ function mt(r) {
248
+ return Re(r), Math.floor(r) * ft;
249
+ }
250
+ function vt(r) {
251
+ return Re(r), Math.floor(r) * pt;
252
+ }
253
+ function Mt(r) {
254
+ return vt(r) * 2;
255
+ }
256
+ function Se(r) {
257
+ var n;
258
+ return ((n = r.backend) == null ? void 0 : n.isWebGPUBackend) === !0;
259
+ }
260
+ class Ct extends l.Mesh {
261
+ constructor(e, o, a = {}) {
262
+ if (!Se(e))
263
+ throw new Error(
264
+ "UnifiedSplatMesh requires a WebGPU backend (renderer.backend.isWebGPUBackend). On WebGL2 use standalone SplatMesh draws or static MergedSplatMesh."
265
+ );
266
+ dt(e, mt(o), o);
267
+ const c = new le(o), g = w(new l.Vector2()), b = w(new l.Vector2()), u = w(3), p = w(0), T = w(0), V = w(0.3), P = w(0), m = w(10), v = w(0), A = Je(), k = w(0), t = w(et), i = w(tt), d = w(rt), B = new l.StorageInstancedBufferAttribute(new Float32Array(o), 1), y = new l.InstancedBufferGeometry();
268
+ y.setIndex([0, 1, 2, 0, 2, 3]), y.setAttribute(
269
+ "position",
270
+ new l.BufferAttribute(new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]), 3)
271
+ ), y.instanceCount = 0;
272
+ super(
273
+ y,
274
+ Pe({
275
+ capacity: o,
276
+ centers: c.centers,
277
+ colors: c.colors,
278
+ covarianceA: c.covarianceA,
279
+ covarianceB: c.covarianceB,
280
+ isotropicMix: c.isotropicMix,
281
+ isotropicScreenRadius: c.isotropicScreenRadius,
282
+ order: B,
283
+ focal: g,
284
+ viewport: b,
285
+ maxStdDev: u,
286
+ minSplatSizePx: p,
287
+ antialias: T,
288
+ projectedLowPassVariance: V,
289
+ compensateProjectedLowPass: P,
290
+ dofFocusDistance: m,
291
+ dofAperture: v,
292
+ relightMap: A,
293
+ relightBlend: k,
294
+ relightBrightness: t,
295
+ relightBackground: i,
296
+ relightSoftness: d
297
+ })
298
+ );
299
+ s(this, "workBuffer");
300
+ s(this, "workSourceIndex");
301
+ /** The sorted draw-order buffer; lives outside the geometry, freed on dispose. */
302
+ s(this, "orderAttribute");
303
+ /**
304
+ * Frees the JS mirrors three keeps behind `workSourceIndex` and `order`.
305
+ *
306
+ * `workSourceIndex` is an identity ramp written once at construction; `order`
307
+ * is written only by the sorter, on the GPU. Neither is ever read back, so
308
+ * both mirrors are 4 B/slot of dead JS heap once uploaded.
309
+ */
310
+ s(this, "mirrors");
311
+ s(this, "sorter");
312
+ s(this, "renderer");
313
+ s(this, "sources", []);
314
+ s(this, "previousLayout", []);
315
+ /** Previous layout keyed by source; kept in step with {@link previousLayout}. */
316
+ s(this, "previousLayoutBySource", /* @__PURE__ */ new Map());
317
+ /** Per-prepare scratch lists, reused so a steady frame allocates nothing. */
318
+ s(this, "candidateScratch", []);
319
+ s(this, "admittedScratch", []);
320
+ s(this, "drawingBufferSize", new l.Vector2());
321
+ /** Reused target dimensions for secondary views; avoids per-view garbage. */
322
+ s(this, "secondaryViewSize", new l.Vector2());
323
+ /**
324
+ * Camera gating for the global sorter, mirroring the standalone
325
+ * `SplatMesh.requestSortIfNeeded` path: an unchanged work buffer under a
326
+ * stationary camera skips the whole-buffer re-sort. Content changes
327
+ * (regather, layout change) force the next sort through the scheduler.
328
+ */
329
+ s(this, "sortScheduler");
330
+ /** Model-view of the last accepted sort; starts unmatchable (zero scale). */
331
+ s(this, "lastSortedModelView", new l.Matrix4().makeScale(0, 0, 0));
332
+ /**
333
+ * Total admitted splats in the last prepared frame. `removeSource` filters
334
+ * {@link previousLayout} directly, so a tail source's removal can leave the
335
+ * remaining entries identical - only this total betrays the shrink, and a
336
+ * shrunk draw range must re-sort (the old permutation indexes past it).
337
+ */
338
+ s(this, "previousAdmittedTotal", 0);
339
+ s(this, "bounds", new l.Sphere());
340
+ s(this, "focal");
341
+ s(this, "viewport");
342
+ s(this, "maxStdDev");
343
+ s(this, "minSplatSizePx");
344
+ s(this, "antialias");
345
+ s(this, "projectedLowPassVariance");
346
+ s(this, "compensateProjectedLowPass");
347
+ s(this, "dofFocusDistance");
348
+ s(this, "dofAperture");
349
+ s(this, "relightPlaceholder");
350
+ s(this, "relightMap");
351
+ s(this, "relightBlend");
352
+ s(this, "relightBrightness");
353
+ s(this, "relightBackground");
354
+ s(this, "relightSoftness");
355
+ s(this, "srgbOutput");
356
+ s(this, "sourceMaxStdDev", null);
357
+ s(this, "sourceAntialias", null);
358
+ s(this, "sourceProjectedFilterProfile", null);
359
+ s(this, "overflowedSourceCount", 0);
360
+ s(this, "overflowedSplatCount", 0);
361
+ s(this, "disposed", !1);
362
+ const f = new Uint32Array(o);
363
+ for (let x = 0; x < o; x++) f[x] = x;
364
+ this.workBuffer = c, this.focal = g, this.viewport = b, this.maxStdDev = u, this.minSplatSizePx = p, this.antialias = T, this.projectedLowPassVariance = V, this.compensateProjectedLowPass = P, this.dofFocusDistance = m, this.dofAperture = v, this.relightPlaceholder = A, this.relightMap = A, this.relightBlend = k, this.relightBrightness = t, this.relightBackground = i, this.relightSoftness = d, this.srgbOutput = a.srgbOutput ?? !1, this.sortScheduler = new st(void 0, ut()), this.orderAttribute = B, this.workSourceIndex = new l.StorageBufferAttribute(f, 1), this.mirrors = new ke([this.workSourceIndex, this.orderAttribute]), this.renderer = e;
365
+ const S = {
366
+ renderer: e,
367
+ capacity: o,
368
+ centersBuffer: c.centers,
369
+ splatIndexAttribute: B,
370
+ sourceIndexAttribute: this.workSourceIndex
371
+ };
372
+ this.sorter = a.sortStrategy === "exact" ? new lt({ ...S, exactDepth: !0 }) : new it(S), this.frustumCulled = !1, this.matrixAutoUpdate = !1, this.matrix.identity(), this.matrixWorld.identity();
373
+ }
374
+ /** Fixed work-buffer splat capacity chosen at construction. */
375
+ get capacity() {
376
+ return this.workBuffer.capacity;
377
+ }
378
+ /** Registers a source. The caller keeps ownership and may still query it. */
379
+ addSource(e, o = {}) {
380
+ if (this.assertNotDisposed("addSource"), this.sources.some((g) => g.source === e)) return;
381
+ const a = e.getUnifiedSourceView(), c = o.visible ?? e.visible;
382
+ if (a.hasSourcePlacement)
383
+ throw new Error(
384
+ "UnifiedSplatMesh: a MergedSplatMesh is already a unified pool with its own global sort; add its sources individually, or draw the mesh directly."
385
+ );
386
+ if (a.srgbOutput !== this.srgbOutput)
387
+ throw new Error("UnifiedSplatMesh: every source must use the renderer's srgbOutput setting.");
388
+ if (this.sourceMaxStdDev === null)
389
+ this.sourceMaxStdDev = a.maxStdDev, this.maxStdDev.value = a.maxStdDev, this.minSplatSizePx.value = a.minSplatSizePx;
390
+ else {
391
+ if (this.sourceMaxStdDev !== a.maxStdDev)
392
+ throw new Error("UnifiedSplatMesh: every source must use the same maxStdDev setting.");
393
+ if (this.minSplatSizePx.value !== a.minSplatSizePx)
394
+ throw new Error("UnifiedSplatMesh: every source must use the same minSplatSizePx setting.");
395
+ }
396
+ if (this.sourceAntialias === null)
397
+ this.sourceAntialias = a.antialias, this.antialias.value = a.antialias ? 1 : 0;
398
+ else if (this.sourceAntialias !== a.antialias)
399
+ throw new Error("UnifiedSplatMesh: every source must use the same antialias setting.");
400
+ if (this.sourceProjectedFilterProfile === null) {
401
+ this.sourceProjectedFilterProfile = a.projectedFilterProfile;
402
+ const g = a.projectedFilterProfile === "lcc";
403
+ this.projectedLowPassVariance.value = g ? 0.1 : 0.3, this.compensateProjectedLowPass.value = g ? 1 : 0;
404
+ } else if (this.sourceProjectedFilterProfile !== a.projectedFilterProfile)
405
+ throw new Error("UnifiedSplatMesh: every source must use the same projected filter profile.");
406
+ this.sources.push({
407
+ source: e,
408
+ gather: this.createGather(a),
409
+ graphRevision: a.graphRevision,
410
+ priority: o.priority ?? 0,
411
+ opacity: o.opacity ?? 1,
412
+ visible: c,
413
+ cacheModifiers: o.cacheModifiers ?? !1,
414
+ originalVisible: e.visible,
415
+ lastGather: null,
416
+ view: null,
417
+ registrationOrder: 0,
418
+ offset: 0
419
+ }), e.visible = !1, e.setUnifiedPickVisibility(c);
420
+ }
421
+ /**
422
+ * Invalidates one registered source's cached gather output.
423
+ *
424
+ * Required after changing modifier uniforms or channels when that source was
425
+ * registered with `cacheModifiers: true`. The following {@link update}
426
+ * re-gathers the source and re-sorts because modifiers may move splats.
427
+ * Returns `false` when the source is not registered.
428
+ */
429
+ invalidateSource(e) {
430
+ this.assertNotDisposed("invalidateSource");
431
+ const o = this.sources.find((a) => a.source === e);
432
+ return o ? (o.lastGather = null, !0) : !1;
433
+ }
434
+ /** Number of whole sources omitted in the most recent update due to capacity. */
435
+ get droppedSourceCount() {
436
+ return this.overflowedSourceCount;
437
+ }
438
+ /** Number of active splats omitted in the most recent update due to capacity. */
439
+ get droppedSplatCount() {
440
+ return this.overflowedSplatCount;
441
+ }
442
+ /** Updates a source's resolved opacity without rebuilding its gather pipeline. */
443
+ setSourceOpacity(e, o) {
444
+ this.assertNotDisposed("setSourceOpacity");
445
+ const a = this.sources.find((c) => c.source === e);
446
+ if (!a) throw new Error("UnifiedSplatMesh: source is not registered.");
447
+ a.opacity = o;
448
+ }
449
+ /** Shows or hides a source without changing its residency. */
450
+ setSourceVisible(e, o) {
451
+ this.assertNotDisposed("setSourceVisible");
452
+ const a = this.sources.find((c) => c.source === e);
453
+ if (!a) throw new Error("UnifiedSplatMesh: source is not registered.");
454
+ a.visible = o, a.source.setUnifiedPickVisibility(o);
455
+ }
456
+ /**
457
+ * Core projected-2D depth of field on the unified draw pass (not gather).
458
+ * Live uniforms - no gather-cache invalidation. Pass `aperture: 0` to disable.
459
+ */
460
+ setDepthOfField(e) {
461
+ this.assertNotDisposed("setDepthOfField");
462
+ const o = ot(e, this.getDepthOfField());
463
+ this.dofFocusDistance.value = o.focusDistance, this.dofAperture.value = o.aperture;
464
+ }
465
+ /** Current core DoF uniforms (`aperture === 0` means off). */
466
+ getDepthOfField() {
467
+ return {
468
+ focusDistance: this.dofFocusDistance.value,
469
+ aperture: this.dofAperture.value
470
+ };
471
+ }
472
+ /**
473
+ * PlayCanvas-style proxy-mesh relighting on the unified draw pass (not gather).
474
+ * Live blend/brightness/background; map identity change rebuilds the draw
475
+ * material. Does not invalidate gather caches. Pass `null` to disable.
476
+ */
477
+ setRelighting(e) {
478
+ if (this.assertNotDisposed("setRelighting"), e === null) {
479
+ this.relightBlend.value = 0, this.relightMap !== this.relightPlaceholder && (this.relightMap = this.relightPlaceholder, this.rebuildDrawMaterial());
480
+ return;
481
+ }
482
+ const o = at(e, this.getRelighting());
483
+ this.relightBlend.value = o.blend, this.relightBrightness.value = o.brightness, this.relightBackground.value = o.background, this.relightSoftness.value = o.softness, e.map !== this.relightMap && (this.relightMap = e.map, this.rebuildDrawMaterial());
484
+ }
485
+ /** Current relight numeric uniforms (`blend === 0` means off). */
486
+ getRelighting() {
487
+ return {
488
+ blend: this.relightBlend.value,
489
+ brightness: this.relightBrightness.value,
490
+ background: this.relightBackground.value,
491
+ softness: this.relightSoftness.value
492
+ };
493
+ }
494
+ /**
495
+ * Asynchronously picks the frontmost visible splat under an NDC coordinate
496
+ * across every registered source.
497
+ *
498
+ * Semantics: each source that is
499
+ * currently visible in this unified renderer runs its own depth-tested GPU
500
+ * pick pass; the hit nearest the camera wins and the result names its source
501
+ * mesh. Hidden sources (`setSourceVisible(source, false)`) never hit. A hit
502
+ * whose source was removed, hidden, or disposed while its readback was in
503
+ * flight is dropped rather than misattributed. Resolves `null` for misses,
504
+ * an empty source list, and picks pending across {@link dispose}.
505
+ *
506
+ * Uses the construction renderer. Concurrent calls are safe: each source
507
+ * serializes its own picks and shared renderer state is restored
508
+ * synchronously before any await.
509
+ */
510
+ async pick(e, o, a) {
511
+ if (this.disposed) return null;
512
+ const c = this.sources.filter((u) => u.visible).map((u) => u.source);
513
+ if (c.length === 0) return null;
514
+ const g = await Promise.all(
515
+ c.map(async (u) => {
516
+ const p = await u.pick(e, o, this.renderer, a);
517
+ return p === null ? null : { source: u, point: p.point, distance: p.distance };
518
+ })
519
+ );
520
+ if (this.disposed) return null;
521
+ let b = null;
522
+ for (const u of g) {
523
+ if (u === null) continue;
524
+ const p = this.sources.find((T) => T.source === u.source);
525
+ p === void 0 || !p.visible || (b === null || u.distance < b.distance) && (b = u);
526
+ }
527
+ return b;
528
+ }
529
+ /** Unregisters a source and restores its standalone draw visibility. */
530
+ removeSource(e) {
531
+ this.assertNotDisposed("removeSource");
532
+ const o = this.sources.findIndex((c) => c.source === e);
533
+ if (o < 0) return !1;
534
+ const [a] = this.sources.splice(o, 1);
535
+ return a && (a.gather.dispose(), a.source.visible = a.originalVisible, a.source.setUnifiedPickVisibility(null), a.lastGather = null), this.previousLayout = this.previousLayout.filter((c) => c.source !== e), this.previousLayoutBySource.delete(e), this.sources.length === 0 && (this.sourceMaxStdDev = null, this.sourceAntialias = null, this.sourceProjectedFilterProfile = null, this.previousLayout = [], this.previousLayoutBySource.clear()), !0;
536
+ }
537
+ /** Updates source LODs, gathers their active splats, then globally sorts them. */
538
+ update(e) {
539
+ this.disposed || this.prepare(e);
540
+ }
541
+ /**
542
+ * Re-sorts and draws this unified source list for a secondary camera.
543
+ * Mirrors and portals use this instead of rendering the hidden source meshes
544
+ * individually, so their reflection keeps one global transparent order.
545
+ */
546
+ renderView(e, o, a = null) {
547
+ if (this.disposed) return;
548
+ if (o !== this.renderer)
549
+ throw new Error("UnifiedSplatMesh: renderView must use its construction renderer.");
550
+ if (!Se(o))
551
+ throw new Error(
552
+ "UnifiedSplatMesh requires a WebGPU backend (renderer.backend.isWebGPUBackend)."
553
+ );
554
+ const c = a ? this.secondaryViewSize.set(a.width, a.height) : void 0;
555
+ this.prepare(e, c, !0);
556
+ const g = o.getRenderTarget();
557
+ try {
558
+ o.setRenderTarget(a), o.render(this, e);
559
+ } finally {
560
+ o.setRenderTarget(g), this.sortScheduler.invalidate();
561
+ }
562
+ }
563
+ prepare(e, o, a = !1) {
564
+ if (!Se(this.renderer))
565
+ throw new Error(
566
+ "UnifiedSplatMesh requires a WebGPU backend (renderer.backend.isWebGPUBackend)."
567
+ );
568
+ this.matrix.identity(), this.matrixWorld.identity();
569
+ const c = o ? null : nt(e, this.renderer), g = (c == null ? void 0 : c.eye) ?? e, b = (c == null ? void 0 : c.head) ?? e, u = this.drawingBufferSize;
570
+ o ? u.copy(o) : c ? u.set(c.width, c.height) : this.renderer.getDrawingBufferSize(u);
571
+ const p = g.projectionMatrix.elements;
572
+ this.viewport.value.copy(u);
573
+ const T = p[0] ?? 0, V = p[5] ?? 0;
574
+ this.focal.value.set(T * u.x / 2, V * u.y / 2);
575
+ const P = this.candidateScratch;
576
+ P.length = 0;
577
+ for (let t = 0; t < this.sources.length; t++) {
578
+ const i = this.sources[t];
579
+ i.source.update(e, this.renderer, { sort: !1 }), i.view = i.source.getUnifiedSourceView(), i.registrationOrder = t, P.push(i);
580
+ }
581
+ P.sort((t, i) => i.priority - t.priority || t.registrationOrder - i.registrationOrder), this.overflowedSourceCount = 0, this.overflowedSplatCount = 0, this.bounds.makeEmpty();
582
+ const m = this.admittedScratch;
583
+ m.length = 0;
584
+ let v = 0;
585
+ for (const t of P) {
586
+ const i = t.view;
587
+ if (t.graphRevision !== i.graphRevision) {
588
+ const d = this.createGather(i);
589
+ t.gather.dispose(), t.gather = d, t.graphRevision = i.graphRevision, t.lastGather = null;
590
+ }
591
+ if (!t.visible) {
592
+ t.lastGather = null;
593
+ continue;
594
+ }
595
+ if (i.activeCount === 0) {
596
+ t.lastGather = null;
597
+ continue;
598
+ }
599
+ if (v + i.activeCount > this.workBuffer.capacity) {
600
+ this.overflowedSourceCount++, this.overflowedSplatCount += i.activeCount, t.lastGather = null;
601
+ continue;
602
+ }
603
+ t.offset = v, m.push(t), this.bounds.union(i.worldBounds), v += i.activeCount;
604
+ }
605
+ let A = !1;
606
+ for (const t of m) {
607
+ const i = t.view, d = t.offset, B = this.previousLayoutBySource.get(t.source), y = B !== void 0 && B.offset === d && B.activeCount === i.activeCount, f = t.lastGather, S = f !== null && y && f.activeCount === i.activeCount && f.contentRevision === i.contentRevision && f.offset === d && f.opacity === t.opacity && f.matrixWorld.equals(i.matrixWorld);
608
+ S && // SH depends on camera position, not orientation. A stationary camera
609
+ // can reuse the resolved colors instead of re-running a full-source
610
+ // gather every frame; camera motion invalidates only SH-bearing slices.
611
+ (i.sh === null || f.localCameraPosition.equals(i.localCameraPosition.value)) && (i.modifiers.length === 0 || t.cacheModifiers) && // Belt-and-braces: a placed pool's splats move without its own
612
+ // `matrixWorld` changing, so a cached gather would go stale silently.
613
+ // `addSource` already rejects these.
614
+ !i.hasSourcePlacement || (t.gather.gather(
615
+ this.renderer,
616
+ i.activeCount,
617
+ d,
618
+ i.matrixWorld,
619
+ b.matrixWorldInverse,
620
+ t.opacity
621
+ ), S || (A = !0), t.lastGather === null ? t.lastGather = {
622
+ activeCount: i.activeCount,
623
+ contentRevision: i.contentRevision,
624
+ offset: d,
625
+ opacity: t.opacity,
626
+ matrixWorld: i.matrixWorld.clone(),
627
+ localCameraPosition: i.localCameraPosition.value.clone()
628
+ } : (t.lastGather.activeCount = i.activeCount, t.lastGather.contentRevision = i.contentRevision, t.lastGather.offset = d, t.lastGather.opacity = t.opacity, t.lastGather.matrixWorld.copy(i.matrixWorld), t.lastGather.localCameraPosition.copy(i.localCameraPosition.value)));
629
+ }
630
+ let k = m.length !== this.previousLayout.length || v !== this.previousAdmittedTotal;
631
+ if (this.previousAdmittedTotal = v, !k)
632
+ for (let t = 0; t < m.length; t++) {
633
+ const i = m[t], d = this.previousLayout[t];
634
+ if (d.source !== i.source || d.offset !== i.offset || d.activeCount !== i.view.activeCount) {
635
+ k = !0;
636
+ break;
637
+ }
638
+ }
639
+ if (k) {
640
+ this.previousLayout = m.map((t) => ({
641
+ source: t.source,
642
+ offset: t.offset,
643
+ activeCount: t.view.activeCount
644
+ })), this.previousLayoutBySource.clear();
645
+ for (const t of this.previousLayout) this.previousLayoutBySource.set(t.source, t);
646
+ }
647
+ if ((A || k) && this.sortScheduler.invalidateContent(), v > 0) {
648
+ const t = performance.now(), i = b.matrixWorldInverse;
649
+ (a || this.sortScheduler.shouldSubmit(i, this.lastSortedModelView, v, t)) && this.sorter.sort(i, v, this.bounds) && (this.lastSortedModelView.copy(i), this.sortScheduler.markAccepted(t));
650
+ }
651
+ this.geometry.instanceCount = v, this.mirrors.settled || this.mirrors.release(this.renderer);
652
+ }
653
+ dispose() {
654
+ if (!this.disposed) {
655
+ this.disposed = !0;
656
+ for (const e of this.sources)
657
+ e.gather.dispose(), e.source.visible = e.originalVisible, e.source.setUnifiedPickVisibility(null), e.lastGather = null;
658
+ this.sources.length = 0, this.previousLayout = [], this.previousLayoutBySource.clear(), this.candidateScratch.length = 0, this.admittedScratch.length = 0, this.sourceMaxStdDev = null, this.sourceAntialias = null, this.sourceProjectedFilterProfile = null, this.sorter.dispose(), this.geometry.dispose(), this.material.dispose(), this.relightPlaceholder.dispose(), ct(this.renderer, [
659
+ this.workBuffer.centers,
660
+ this.workBuffer.colors,
661
+ this.workBuffer.covarianceA,
662
+ this.workBuffer.covarianceB,
663
+ this.workBuffer.isotropicMix,
664
+ this.workBuffer.isotropicScreenRadius,
665
+ this.workSourceIndex,
666
+ this.orderAttribute
667
+ ]);
668
+ }
669
+ }
670
+ assertNotDisposed(e) {
671
+ if (this.disposed)
672
+ throw new Error(`UnifiedSplatMesh: ${e} called after dispose.`);
673
+ }
674
+ /** Rebuilds the EWA draw material when the relight map identity changes. */
675
+ rebuildDrawMaterial() {
676
+ const e = this.material;
677
+ this.material = Pe({
678
+ capacity: this.workBuffer.capacity,
679
+ centers: this.workBuffer.centers,
680
+ colors: this.workBuffer.colors,
681
+ covarianceA: this.workBuffer.covarianceA,
682
+ covarianceB: this.workBuffer.covarianceB,
683
+ isotropicMix: this.workBuffer.isotropicMix,
684
+ isotropicScreenRadius: this.workBuffer.isotropicScreenRadius,
685
+ order: this.orderAttribute,
686
+ focal: this.focal,
687
+ viewport: this.viewport,
688
+ maxStdDev: this.maxStdDev,
689
+ minSplatSizePx: this.minSplatSizePx,
690
+ antialias: this.antialias,
691
+ projectedLowPassVariance: this.projectedLowPassVariance,
692
+ compensateProjectedLowPass: this.compensateProjectedLowPass,
693
+ dofFocusDistance: this.dofFocusDistance,
694
+ dofAperture: this.dofAperture,
695
+ relightMap: this.relightMap,
696
+ relightBlend: this.relightBlend,
697
+ relightBrightness: this.relightBrightness,
698
+ relightBackground: this.relightBackground,
699
+ relightSoftness: this.relightSoftness
700
+ }), e.dispose();
701
+ }
702
+ createGather(e) {
703
+ const o = this.buildGather(e);
704
+ return o.warmUp(this.renderer).catch(() => {
705
+ }), o;
706
+ }
707
+ buildGather(e) {
708
+ return new ht({
709
+ capacity: this.workBuffer.capacity,
710
+ sourceCapacity: e.capacity,
711
+ sourceIndex: e.sourceIndex,
712
+ centersTexture: e.centersTexture,
713
+ colorsTexture: e.colorsTexture,
714
+ covarianceATexture: e.covarianceATexture,
715
+ covarianceBTexture: e.covarianceBTexture,
716
+ dataTextureWidth: e.dataTextureWidth,
717
+ sh: e.sh,
718
+ modifiers: e.modifiers,
719
+ channels: e.channels,
720
+ localCameraPosition: e.localCameraPosition,
721
+ srgbOutput: this.srgbOutput,
722
+ // Per source, not a compatibility field: the gather normalizes `.rad`
723
+ // half-alpha here so one shared draw material serves mixed scenes.
724
+ lodAlpha: e.lodAlpha,
725
+ workBuffer: this.workBuffer
726
+ });
727
+ }
728
+ }
729
+ export {
730
+ Ct as UnifiedSplatMesh,
731
+ pt as WORK_BUFFER_BYTES_PER_SLOT,
732
+ ft as WORK_BUFFER_CENTERS_BYTES_PER_SPLAT,
733
+ mt as estimateLargestStorageBufferBytes,
734
+ vt as estimateUnifiedWorkBufferBytes,
735
+ Mt as estimateUnifiedWorkBufferPeakBytes,
736
+ Se as supportsUnifiedSplatMesh
737
+ };
738
+ //# sourceMappingURL=unified.js.map