@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,969 @@
1
+ var St = Object.defineProperty;
2
+ var Tt = (s, e, t) => e in s ? St(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
3
+ var l = (s, e, t) => Tt(s, typeof e != "symbol" ? e + "" : e, t);
4
+ import * as h from "three/webgpu";
5
+ import { bool as yt, mat3 as Te, vec3 as S, float as v, modelViewMatrix as $e, vec4 as K, modelWorldMatrix as At, int as L, attribute as Mt, ivec2 as je, textureLoad as O, varying as he, colorSpaceToWorking as Ct, positionGeometry as _e, Fn as G, cameraProjectionMatrix as Pt, If as pe, mix as oe, vec2 as J, Discard as Ne, texture as ve, screenUV as Se, uniform as fe, uint as C, storage as ne, atomicStore as qe, instanceIndex as E, atomicAdd as rt, Loop as We, atomicLoad as it } from "three/tsl";
6
+ import { w as kt } from "./logging-BfPdd7NJ.js";
7
+ function Rt(s, e, t) {
8
+ if (s.length === 0)
9
+ return {
10
+ color: t.color,
11
+ offset: null,
12
+ scaleSquared: null,
13
+ rotation: null,
14
+ visible: null,
15
+ isotropicCovarianceMix: null,
16
+ isotropicVarianceScale: null,
17
+ isotropicScreenRadiusPx: null
18
+ };
19
+ const o = e, a = {
20
+ color: t.color,
21
+ offset: S(0, 0, 0),
22
+ scale: v(1),
23
+ rotation: Te(S(1, 0, 0), S(0, 1, 0), S(0, 0, 1)),
24
+ visible: yt(!0)
25
+ }, n = { offset: !1, scale: !1, rotation: !1, visible: !1 };
26
+ let r = null, i = null, d = null, c = null, T = null, D = null;
27
+ const y = /* @__PURE__ */ new Map(), q = {
28
+ index: t.index,
29
+ localCenter: t.localCenter,
30
+ sourceCenter: t.sourceCenter ?? t.localCenter,
31
+ sourceToLocal: t.sourceToLocal ?? a.rotation,
32
+ cameraLocal: o,
33
+ baseColor: t.color,
34
+ get worldCenter() {
35
+ return c ?? (c = t.makeWorldCenter ? t.makeWorldCenter() : At.mul(K(t.localCenter, 1)).xyz);
36
+ },
37
+ get viewCenter() {
38
+ return T ?? (T = t.makeViewCenter ? t.makeViewCenter() : $e.mul(K(t.localCenter, 1)).xyz);
39
+ },
40
+ get normal() {
41
+ return D ?? (D = t.makeNormal());
42
+ },
43
+ channel(M) {
44
+ let m = y.get(M);
45
+ return m || (m = t.makeChannel(M), y.set(M, m)), m;
46
+ },
47
+ get color() {
48
+ return a.color;
49
+ },
50
+ get offset() {
51
+ return a.offset;
52
+ },
53
+ get scale() {
54
+ return a.scale;
55
+ },
56
+ get rotation() {
57
+ return a.rotation;
58
+ },
59
+ get visible() {
60
+ return a.visible;
61
+ }
62
+ };
63
+ for (const M of s) {
64
+ const m = M(q);
65
+ m.color && (a.color = m.color), m.offset && (a.offset = m.offset, n.offset = !0), m.scale && (a.scale = m.scale, n.scale = !0), m.rotation && (a.rotation = m.rotation, n.rotation = !0), m.visible && (a.visible = m.visible, n.visible = !0), m.isotropicCovarianceMix !== void 0 && (r = m.isotropicCovarianceMix), m.isotropicVarianceScale !== void 0 && (i = m.isotropicVarianceScale), m.isotropicScreenRadiusPx !== void 0 && (d = m.isotropicScreenRadiusPx);
66
+ }
67
+ return {
68
+ color: a.color,
69
+ offset: n.offset ? a.offset : null,
70
+ scaleSquared: n.scale ? a.scale.mul(a.scale) : null,
71
+ rotation: n.rotation ? a.rotation : null,
72
+ visible: n.visible ? a.visible : null,
73
+ isotropicCovarianceMix: r,
74
+ isotropicVarianceScale: i,
75
+ isotropicScreenRadiusPx: d
76
+ };
77
+ }
78
+ const De = 2048;
79
+ function Bt(s, e, t) {
80
+ let o = -1;
81
+ for (let a = 0; a < s.length; a++) {
82
+ const n = s[a];
83
+ if (!(n.count < e) && ((o === -1 || n.count < s[o].count) && (o = a), n.count === e))
84
+ break;
85
+ }
86
+ if (o !== -1) {
87
+ const a = s[o], n = a.start;
88
+ return a.start += e, a.count -= e, a.count === 0 && s.splice(o, 1), n;
89
+ }
90
+ throw new Error(
91
+ `SplatMesh capacity exceeded: no contiguous space for ${e} rows (${De} splats each) in a ${t}-row pool.`
92
+ );
93
+ }
94
+ function It(s, e, t) {
95
+ s.push({ start: e, count: t }), s.sort((a, n) => a.start - n.start);
96
+ const o = [];
97
+ for (const a of s) {
98
+ const n = o[o.length - 1];
99
+ if (n && n.start + n.count > a.start)
100
+ throw new Error(
101
+ `SplatMesh pool released rows [${e}, ${e + t}) overlap an already-free span.`
102
+ );
103
+ n && n.start + n.count === a.start ? n.count += a.count : o.push(a);
104
+ }
105
+ return o;
106
+ }
107
+ class is {
108
+ constructor(e) {
109
+ l(this, "width", De);
110
+ l(this, "rows");
111
+ l(this, "floatTextures");
112
+ l(this, "packedShBands");
113
+ l(this, "backing");
114
+ l(this, "centersTexture");
115
+ l(this, "colorsTexture");
116
+ l(this, "covarianceATexture");
117
+ l(this, "covarianceBTexture");
118
+ l(this, "shPackedTextures");
119
+ /** Free row spans, sorted by start row. Mutated in place by the allocator. */
120
+ l(this, "freeRowSpans");
121
+ l(this, "tenants", /* @__PURE__ */ new Set());
122
+ /**
123
+ * Pool index → its owner's packed active-list slot, valid only while that
124
+ * pool index is active.
125
+ *
126
+ * Pool-owned rather than per-mesh because it is keyed by *pool* index: a
127
+ * per-mesh copy would have to span the whole pool, so N meshes sharing one
128
+ * pool would each pay 4 B per pool splat (a 4 M-splat pool shared by 13
129
+ * meshes = ~200 MB of reverse maps). One array is safe because a row belongs
130
+ * to exactly one tenant at a time, so no two tenants ever write the same
131
+ * entry - the slot numbers in it are simply read back by whoever owns
132
+ * the row.
133
+ */
134
+ l(this, "activeSlotByPoolIndex");
135
+ l(this, "poolIndexTemplate", null);
136
+ if (!(e.capacity > 0)) throw new Error("SplatPool capacity must be positive.");
137
+ this.rows = Math.ceil(e.capacity / this.width), Dt(this.rows, e.maxTextureSize ?? 0, this.width);
138
+ const t = this.width * this.rows;
139
+ this.floatTextures = e.floatTextures === "float16" ? "float16" : "float32", this.packedShBands = e.packedShBands ?? 0;
140
+ const o = this.floatTextures === "float16" ? h.HalfFloatType : h.FloatType;
141
+ this.backing = {
142
+ centers: new Float32Array(t * 4),
143
+ colors: new Uint8Array(t * 4),
144
+ covarianceA: new Float32Array(t * 4),
145
+ covarianceB: new Float32Array(t * 4),
146
+ // One RGBA32UI texel per splat per texture; sharing the pool's row
147
+ // geometry lets the whole partial-upload/compaction path reuse the
148
+ // existing row arithmetic unchanged.
149
+ shPacked: Array.from(
150
+ { length: e.packedShTextureCount ?? 0 },
151
+ () => new Uint32Array(t * 4)
152
+ )
153
+ };
154
+ const a = this.floatTextures === "float16" ? new Uint16Array(t * 4) : this.backing.centers, n = this.floatTextures === "float16" ? new Uint16Array(t * 4) : this.backing.covarianceA;
155
+ this.centersTexture = Ee(a, this.width, this.rows, o), this.colorsTexture = Ee(
156
+ this.backing.colors,
157
+ this.width,
158
+ this.rows,
159
+ h.UnsignedByteType
160
+ ), this.covarianceATexture = Ee(n, this.width, this.rows, o), this.covarianceBTexture = Ee(
161
+ this.backing.covarianceB,
162
+ this.width,
163
+ this.rows,
164
+ h.FloatType
165
+ ), this.shPackedTextures = this.backing.shPacked.map(
166
+ (r) => Et(r, this.width, this.rows)
167
+ ), this.freeRowSpans = [{ start: 0, count: this.rows }], this.activeSlotByPoolIndex = new Uint32Array(t);
168
+ }
169
+ /**
170
+ * A reusable `0, 1, 2, …` ramp over the pool, used to fill runs of pool
171
+ * indices and active-list slots without a per-element loop. Pool-owned so
172
+ * tenants sharing a pool share the one copy.
173
+ */
174
+ indexTemplate() {
175
+ if (this.poolIndexTemplate) return this.poolIndexTemplate;
176
+ const e = new Uint32Array(this.capacity);
177
+ for (let t = 0; t < e.length; t++) e[t] = t;
178
+ return this.poolIndexTemplate = e, e;
179
+ }
180
+ /** Splats the pool holds, always a whole number of rows. */
181
+ get capacity() {
182
+ return this.rows * this.width;
183
+ }
184
+ /** Whether `texture` belongs to this pool (and so outlives any one tenant). */
185
+ isPoolTexture(e) {
186
+ return e === this.centersTexture || e === this.colorsTexture || e === this.covarianceATexture || e === this.covarianceBTexture || this.shPackedTextures.includes(e);
187
+ }
188
+ /** The four core textures, in the order the upload path indexes them. */
189
+ get coreTextures() {
190
+ return [
191
+ this.centersTexture,
192
+ this.colorsTexture,
193
+ this.covarianceATexture,
194
+ this.covarianceBTexture
195
+ ];
196
+ }
197
+ /** Rows still free, whether or not they are contiguous. */
198
+ get freeRows() {
199
+ let e = 0;
200
+ for (const t of this.freeRowSpans) e += t.count;
201
+ return e;
202
+ }
203
+ /** Takes `rowCount` contiguous rows; throws when none are left (compact cue). */
204
+ allocateRows(e) {
205
+ return Bt(this.freeRowSpans, e, this.rows);
206
+ }
207
+ /** Returns rows to the free list, coalescing neighbours. */
208
+ releaseRows(e, t) {
209
+ this.freeRowSpans = It(this.freeRowSpans, e, t);
210
+ }
211
+ /** Resets the free list to one span covering the whole pool. */
212
+ resetFreeRows(e = 0) {
213
+ this.freeRowSpans = e < this.rows ? [{ start: e, count: this.rows - e }] : [];
214
+ }
215
+ /**
216
+ * Adds a mesh that draws from this pool. Registration exists for
217
+ * {@link compact}: packing the pool moves rows that belong to *every*
218
+ * tenant, so each has to be told where its ranges went.
219
+ */
220
+ register(e) {
221
+ this.tenants.add(e);
222
+ }
223
+ /**
224
+ * Drops a tenant. Release its ranges first: an unregistered tenant is
225
+ * invisible to {@link compact}, which would then treat rows it still holds
226
+ * as free and hand them to someone else. `compact` throws rather than let
227
+ * that happen silently.
228
+ */
229
+ unregister(e) {
230
+ this.tenants.delete(e);
231
+ }
232
+ /** Number of meshes drawing from this pool. */
233
+ get tenantCount() {
234
+ return this.tenants.size;
235
+ }
236
+ /**
237
+ * Packs every tenant's ranges toward row 0, removing the gaps that add /
238
+ * remove churn leaves behind and restoring a single contiguous free span.
239
+ * Callers reach this when {@link allocateRows} throws despite enough total
240
+ * free rows - row-alignment fragmentation.
241
+ *
242
+ * The CPU backing arrays are authoritative, so nothing is re-fetched; moved
243
+ * rows are re-uploaded on the tenant's next update.
244
+ *
245
+ * Note this is a whole-pool stall: with several tenants, one mesh's
246
+ * fragmentation relocates the others' rows too, and every tenant rebuilds
247
+ * its draw state. (Spark sidesteps the equivalent by allocating fixed-size,
248
+ * interchangeable pages that never need packing; vlam's ranges are variable
249
+ * runs, so it packs instead.)
250
+ */
251
+ compact() {
252
+ const e = this.width, t = [];
253
+ let o = 0;
254
+ for (const n of this.tenants)
255
+ for (const r of n.poolRanges())
256
+ t.push({ tenant: n, range: r }), o += r.rowCount;
257
+ if (o + this.freeRows !== this.rows)
258
+ throw new Error(
259
+ `SplatPool compaction found ${this.rows - o - this.freeRows} unaccounted row(s): ${o} held by ${this.tenants.size} tenant(s), ${this.freeRows} free, ${this.rows} total. Release a tenant's ranges before unregistering it.`
260
+ );
261
+ t.sort((n, r) => n.range.startRow - r.range.startRow);
262
+ let a = 0;
263
+ for (const { tenant: n, range: r } of t) {
264
+ if (r.startRow !== a) {
265
+ const i = r.startRow * e * 4, d = a * e * 4, c = r.rowCount * e * 4;
266
+ this.backing.centers.copyWithin(d, i, i + c), this.backing.colors.copyWithin(d, i, i + c), this.backing.covarianceA.copyWithin(d, i, i + c), this.backing.covarianceB.copyWithin(d, i, i + c);
267
+ for (const T of this.backing.shPacked) T.copyWithin(d, i, i + c);
268
+ n.relocatePoolRange(r, a);
269
+ }
270
+ a += r.rowCount;
271
+ }
272
+ this.resetFreeRows(a);
273
+ for (const n of this.tenants) n.onPoolCompacted();
274
+ }
275
+ dispose() {
276
+ for (const e of this.coreTextures) e.dispose();
277
+ for (const e of this.shPackedTextures) e.dispose();
278
+ this.tenants.clear();
279
+ }
280
+ }
281
+ function Ee(s, e, t, o) {
282
+ const a = new h.DataTexture(s, e, t, h.RGBAFormat, o);
283
+ return a.needsUpdate = !0, a;
284
+ }
285
+ function Et(s, e, t) {
286
+ const o = new h.DataTexture(
287
+ s,
288
+ e,
289
+ t,
290
+ h.RGBAIntegerFormat,
291
+ h.UnsignedIntType
292
+ );
293
+ return o.magFilter = h.NearestFilter, o.minFilter = h.NearestFilter, o.generateMipmaps = !1, o.needsUpdate = !0, o;
294
+ }
295
+ function cs(s) {
296
+ var a, n;
297
+ const e = s.backend, t = (n = (a = e == null ? void 0 : e.device) == null ? void 0 : a.limits) == null ? void 0 : n.maxTextureDimension2D;
298
+ if (typeof t == "number" && t > 0) return t;
299
+ const o = e == null ? void 0 : e.gl;
300
+ if (o) {
301
+ const r = o.getParameter(o.MAX_TEXTURE_SIZE);
302
+ if (typeof r == "number" && r > 0) return r;
303
+ }
304
+ return 0;
305
+ }
306
+ function Dt(s, e, t = De) {
307
+ if (!(!(e > 0) || s <= e))
308
+ throw new Error(
309
+ `SplatPool: this scene needs a ${t}×${s} data texture, but this device caps texture dimensions at ${e} (about ${(e * t).toLocaleString("en-US")} pool splats). Lower the splat budget, or raise maxTextureDimension2D in the renderer's requiredLimits if the adapter advertises more.`
310
+ );
311
+ }
312
+ function ls(s, e, t) {
313
+ let o = e, a = e + t;
314
+ const n = [];
315
+ for (const r of s.updateRanges) {
316
+ const i = r.start + r.count;
317
+ if (i < o || r.start > a) {
318
+ n.push(r);
319
+ continue;
320
+ }
321
+ o = Math.min(o, r.start), a = Math.max(a, i);
322
+ }
323
+ n.push({ start: o, count: a - o }), n.sort((r, i) => r.start - i.start), s.clearUpdateRanges();
324
+ for (const r of n) s.addUpdateRange(r.start, r.count);
325
+ }
326
+ const ct = 24, dt = ct * ct;
327
+ function Ft(s, e) {
328
+ const t = Math.max(0, s);
329
+ if (t <= 0) return 0;
330
+ const o = Math.max(1e-4, e);
331
+ return 2 * Math.atan(0.5 * t / o);
332
+ }
333
+ function us(s) {
334
+ const e = Math.max(1e-4, s.depth), t = Math.max(1e-4, s.focusDistance), o = Ft(s.aperture, t);
335
+ if (o <= 0 || !(s.focalPx > 0)) return 0;
336
+ const a = Math.abs(e - t) / e, n = s.focalPx * Math.tan(0.5 * o), r = a * n, i = r * r, d = s.maxVariance ?? dt;
337
+ return Math.min(i, d);
338
+ }
339
+ function ds(s, e) {
340
+ return Math.sqrt(Math.max(0, s) / Math.max(1e-9, e));
341
+ }
342
+ function ms(s, e = { focusDistance: 10, aperture: 0 }) {
343
+ const t = typeof s.focusDistance == "number" && Number.isFinite(s.focusDistance) ? Math.max(1e-4, s.focusDistance) : e.focusDistance, o = typeof s.aperture == "number" && Number.isFinite(s.aperture) ? Math.max(0, s.aperture) : e.aperture;
344
+ return { focusDistance: t, aperture: o };
345
+ }
346
+ const lt = 1024, zt = 1e30, Ut = 0.35 * 0.35;
347
+ function Vt(s, e, t, o) {
348
+ const a = S(1, 0, 0), n = S(0, 1, 0), r = S(0, 0, 1), i = s.mul(a), d = s.mul(n), c = s.mul(r), T = i.x, D = d.y, y = c.z, q = i.cross(d), M = d.cross(c), m = c.cross(i), w = q.length(), V = M.length(), X = m.length(), xe = w.greaterThanEqual(V).and(w.greaterThanEqual(X)), ae = V.greaterThan(w).and(V.greaterThanEqual(X)), re = xe.select(q, ae.select(M, m)), u = re.div(re.length().max(1e-12)), x = u.dot(s.mul(u)), F = T.min(D).min(y).min(x).max(1e-12).mul(o), $ = Te(S(F, 0, 0), S(0, F, 0), S(0, 0, F));
349
+ return s.mul(v(1).sub(t)).add($.mul(t));
350
+ }
351
+ function Ot(s, e, t) {
352
+ const o = s.min(e);
353
+ return {
354
+ lambda1: oe(s, o, t),
355
+ lambda2: oe(e, o, t)
356
+ };
357
+ }
358
+ function Lt(s, e, t, o, a) {
359
+ const n = o.div(a).pow(2), r = s.min(e).min(n), i = oe(s, r, t), d = oe(e, r, t), c = o.greaterThan(0);
360
+ return {
361
+ lambda1: c.select(i, s),
362
+ lambda2: c.select(d, e)
363
+ };
364
+ }
365
+ function hs(s) {
366
+ return s;
367
+ }
368
+ function fs() {
369
+ return fe(new h.Vector3());
370
+ }
371
+ function mt(s, e, t) {
372
+ const o = e.mul(L(4)), a = s.element(o), n = s.element(o.add(L(1))), r = s.element(o.add(L(2))), i = s.element(o.add(L(3))), d = a.xyz.mul(t.x).add(n.xyz.mul(t.y)).add(r.xyz.mul(t.z)).add(i.xyz), c = Te(a.xyz, n.xyz, r.xyz);
373
+ return { worldCenter: d, linear: c };
374
+ }
375
+ function _t(s) {
376
+ return [0, 3, 8, 15][s] ?? 0;
377
+ }
378
+ function ht(s, e, t) {
379
+ if (s.mode === "palette") {
380
+ const n = O(e.covarianceBTexture, t).z.toInt(), r = n.mod(L(64)).mul(L(_t(s.bands))), i = n.div(L(64));
381
+ return (d) => O(s.paletteTexture, je(r.add(L(d)), i)).xyz;
382
+ }
383
+ const o = s.textures.map(
384
+ (n) => O(n, t).toVar()
385
+ ), a = s.range.max.sub(s.range.min);
386
+ return (n) => {
387
+ const r = o[n >> 2], i = [r.x, r.y, r.z, r.w][n & 3], d = S(
388
+ i.bitAnd(C(2047)).toFloat().div(2047),
389
+ i.shiftRight(C(11)).bitAnd(C(1023)).toFloat().div(1023),
390
+ i.shiftRight(C(21)).bitAnd(C(2047)).toFloat().div(2047)
391
+ );
392
+ return s.range.min.add(a.mul(d));
393
+ };
394
+ }
395
+ const ge = 0.4886025119029199, H = [
396
+ 1.0925484305920792,
397
+ -1.0925484305920792,
398
+ 0.31539156525252005,
399
+ -1.0925484305920792,
400
+ 0.5462742152960396
401
+ ], U = [
402
+ -0.5900435899266435,
403
+ 2.890611442640554,
404
+ -0.4570457994644658,
405
+ 0.3731763325901154,
406
+ -0.4570457994644658,
407
+ 1.445305721320277,
408
+ -0.5900435899266435
409
+ ];
410
+ function ps(s, e, t, o) {
411
+ const a = o.x, n = o.y, r = o.z, i = ht(s, e, t), d = i(0).mul(n.mul(-ge)).add(i(1).mul(r.mul(ge))).add(i(2).mul(a.mul(-ge)));
412
+ if (s.bands === 1) return d;
413
+ const c = a.mul(a), T = n.mul(n), D = r.mul(r), y = d.add(i(3).mul(a.mul(n).mul(H[0]))).add(i(4).mul(n.mul(r).mul(H[1]))).add(i(5).mul(D.mul(2).sub(c).sub(T).mul(H[2]))).add(i(6).mul(a.mul(r).mul(H[3]))).add(i(7).mul(c.sub(T).mul(H[4])));
414
+ return s.bands === 2 ? y : y.add(i(8).mul(n.mul(c.mul(3).sub(T)).mul(U[0]))).add(i(9).mul(a.mul(n).mul(r).mul(U[1]))).add(i(10).mul(n.mul(D.mul(4).sub(c).sub(T)).mul(U[2]))).add(i(11).mul(r.mul(D.mul(2).sub(c.mul(3)).sub(T.mul(3))).mul(U[3]))).add(i(12).mul(a.mul(D.mul(4).sub(c).sub(T)).mul(U[4]))).add(i(13).mul(r.mul(c.sub(T)).mul(U[5]))).add(i(14).mul(a.mul(c.sub(T.mul(3))).mul(U[6])));
415
+ }
416
+ function gs(s, e, t) {
417
+ const { textures: o, sh: a, uniforms: n, settings: r, pick: i } = t, d = L(De), c = v(lt), T = -0.5 * r.maxStdDev * r.maxStdDev, D = Mt("splatIndex", "float").toInt(), y = je(D.mod(d), D.div(d)), q = O(o.colorsTexture, y), M = O(o.centersTexture, y).xyz, m = t.sourcePlacement, w = m ? mt(
418
+ m.columns,
419
+ O(m.sourceIdTexture, y).r.toInt(),
420
+ M
421
+ ) : null, V = w ? w.worldCenter : M, X = a === null ? null : (() => {
422
+ const b = w ? w.linear.inverse().mul(V.sub(n.localCameraPosition)).normalize() : V.sub(n.localCameraPosition).normalize(), f = b.x, g = b.y, B = b.z, p = ht(a, o, y), z = p(0).mul(g.mul(-ge)).add(p(1).mul(B.mul(ge))).add(p(2).mul(f.mul(-ge)));
423
+ if (a.bands === 1) return z;
424
+ const A = f.mul(f), I = g.mul(g), P = B.mul(B), _ = z.add(p(3).mul(f.mul(g).mul(H[0]))).add(p(4).mul(g.mul(B).mul(H[1]))).add(p(5).mul(P.mul(2).sub(A).sub(I).mul(H[2]))).add(p(6).mul(f.mul(B).mul(H[3]))).add(p(7).mul(A.sub(I).mul(H[4])));
425
+ return a.bands === 2 ? _ : _.add(p(8).mul(g.mul(A.mul(3).sub(I)).mul(U[0]))).add(p(9).mul(f.mul(g).mul(B).mul(U[1]))).add(p(10).mul(g.mul(P.mul(4).sub(A).sub(I)).mul(U[2]))).add(
426
+ p(11).mul(
427
+ B.mul(P.mul(2).sub(A.mul(3)).sub(I.mul(3))).mul(U[3])
428
+ )
429
+ ).add(p(12).mul(f.mul(P.mul(4).sub(A).sub(I)).mul(U[4]))).add(p(13).mul(B.mul(A.sub(I)).mul(U[5]))).add(p(14).mul(f.mul(A.sub(I.mul(3))).mul(U[6])));
430
+ })(), xe = X === null ? q : K(q.rgb.add(X).clamp(0, 1), q.a), ae = () => {
431
+ const b = O(o.covarianceATexture, y), f = O(o.covarianceBTexture, y), g = Te(
432
+ S(b.x.add(1e-8), b.y, b.z),
433
+ S(b.y, b.w.add(1e-8), f.x),
434
+ S(b.z, f.x, f.y.add(1e-8))
435
+ ), p = (w ? w.linear.mul(g).mul(w.linear.transpose()) : g).inverse(), z = n.localCameraPosition.sub(V), A = p.mul(p.mul(z)).normalize();
436
+ return A.mul(A.dot(z).sign());
437
+ }, re = (b) => {
438
+ const f = t.channels.get(b);
439
+ if (!f)
440
+ throw new Error(
441
+ `SplatMesh: a modifier reads channel "${b}", which is not defined. Call defineChannel("${b}") before assigning the modifier.`
442
+ );
443
+ return O(f.texture, y).r;
444
+ }, u = Rt(t.modifiers, n.localCameraPosition, {
445
+ index: D,
446
+ localCenter: V,
447
+ sourceCenter: M,
448
+ sourceToLocal: w == null ? void 0 : w.linear,
449
+ color: xe,
450
+ makeNormal: ae,
451
+ makeChannel: re
452
+ }), x = he(
453
+ r.srgbOutput ? u.color : Ct(u.color, h.SRGBColorSpace)
454
+ ), R = he(_e.xy), F = e === "pick" ? he(v(0), "pickViewDepth") : null, $ = he(v(1), "vOpacityCompensation"), Z = r.lodAlpha ? he(v(r.maxStdDev), "vAdjustedStdDev") : null, ie = r.lodAlpha ? he(v(1), "vAlpha2") : null;
455
+ if (s.vertexNode = G(() => {
456
+ const b = u.offset === null ? V : V.add(u.offset), f = $e.mul(K(b, 1)).toVar(), g = Pt.mul(f).toVar(), B = K(0, 0, 2, 1).toVar(), p = e === "pick" ? 1.2 + 2 * lt : 1.2, z = g.w.mul(p), A = g.z.greaterThan(z.negate()).and(g.x.abs().lessThan(z)).and(g.y.abs().lessThan(z)), I = u.visible === null ? A : A.and(u.visible);
457
+ return pe(I, () => {
458
+ F && F.assign(f.z.negate());
459
+ const P = O(o.covarianceATexture, y), _ = O(o.covarianceBTexture, y), ce = Te(
460
+ S(P.x, P.y, P.z),
461
+ S(P.y, P.w, _.x),
462
+ S(P.z, _.x, _.y)
463
+ ), be = w ? w.linear.mul(ce).mul(w.linear.transpose()) : ce;
464
+ let Y = u.rotation === null ? be : u.rotation.mul(be).mul(u.rotation.transpose());
465
+ if (u.isotropicCovarianceMix !== null) {
466
+ const k = u.isotropicVarianceScale ?? v(Ut);
467
+ Y = Vt(
468
+ Y,
469
+ S(0, 0, 1),
470
+ u.isotropicCovarianceMix,
471
+ k
472
+ );
473
+ }
474
+ const le = v(1).div(f.z), ye = le.mul(le), Ae = S(
475
+ n.focal.x.mul(le),
476
+ 0,
477
+ n.focal.x.negate().mul(f.x).mul(ye)
478
+ ), Fe = S(
479
+ 0,
480
+ n.focal.y.mul(le),
481
+ n.focal.y.negate().mul(f.y).mul(ye)
482
+ ), Me = $e.toMat3().transpose(), ue = Me.mul(Ae), de = Me.mul(Fe), Ce = ue.dot(Y.mul(ue)), Pe = de.dot(Y.mul(de)), He = ue.dot(Y.mul(de)), ze = u.scaleSquared === null ? Ce : Ce.mul(u.scaleSquared), Ue = u.scaleSquared === null ? Pe : Pe.mul(u.scaleSquared), we = u.scaleSquared === null ? He : He.mul(u.scaleSquared), Ke = f.z.negate().max(1e-4), Xe = n.dofFocusDistance.max(1e-4), ft = n.dofAperture.max(0).mul(0.5).div(Xe).atan(), pt = Ke.sub(Xe).abs().div(Ke), gt = n.focal.x.mul(ft.tan()), Ze = pt.mul(gt), Qe = Ze.mul(Ze).min(v(dt)), ke = r.projectedFilterProfile === "lcc" ? 0.1 : 0.3, Re = ze.add(ke).add(Qe), Ve = Ue.add(ke).add(Qe), Be = we, xt = Re.mul(Ve).sub(Be.mul(Be)).max(1e-9), Je = (r.antialias || r.projectedFilterProfile === "lcc" ? ze.mul(Ue).sub(we.mul(we)).max(0) : ze.add(ke).mul(Ue.add(ke)).sub(we.mul(we)).max(1e-9)).div(xt).sqrt();
483
+ $.assign(
484
+ u.isotropicCovarianceMix === null ? Je : oe(Je, v(1), u.isotropicCovarianceMix)
485
+ );
486
+ const Ye = Re.add(Ve).mul(0.5), et = J(Re.sub(Ve).mul(0.5), Be).length();
487
+ let Q = Ye.add(et), me = Ye.sub(et).max(0);
488
+ if (u.isotropicCovarianceMix !== null) {
489
+ const k = Ot(
490
+ Q,
491
+ me,
492
+ u.isotropicCovarianceMix
493
+ );
494
+ if (Q = k.lambda1, me = k.lambda2, u.isotropicScreenRadiusPx !== null) {
495
+ const N = Lt(
496
+ Q,
497
+ me,
498
+ u.isotropicCovarianceMix,
499
+ u.isotropicScreenRadiusPx,
500
+ v(r.maxStdDev)
501
+ );
502
+ Q = N.lambda1, me = N.lambda2;
503
+ }
504
+ }
505
+ const Oe = J(Be, Q.sub(Re)).add(J(1e-6, 0)).normalize();
506
+ let Ie = v(r.maxStdDev);
507
+ if (r.lodAlpha && Z && ie) {
508
+ const k = u.color.a.mul(2), N = k.mul(4).sub(3).min(5);
509
+ Ie = k.greaterThan(1).select(
510
+ v(r.maxStdDev).add(N.sub(1).mul(0.7)),
511
+ v(r.maxStdDev)
512
+ ), Z.assign(Ie), ie.assign(k);
513
+ }
514
+ const bt = r.maxAspect && r.maxAspect > 0 ? Q.min(me.mul(r.maxAspect * r.maxAspect)) : Q, tt = v(r.minSplatSizePx ?? 0), st = Oe.mul(
515
+ bt.sqrt().mul(Ie).min(c).max(tt)
516
+ ), nt = J(Oe.y, Oe.x.negate()).mul(
517
+ me.sqrt().mul(Ie).min(c).max(tt)
518
+ ), Le = () => {
519
+ const k = st.mul(_e.x).add(nt.mul(_e.y)), N = g.xy.div(g.w);
520
+ B.assign(
521
+ K(
522
+ N.add(k.mul(2).div(n.viewport)),
523
+ g.z.div(g.w),
524
+ 1
525
+ )
526
+ );
527
+ };
528
+ let ee;
529
+ if (r.foveationMode === "page-table")
530
+ ee = null;
531
+ else if (r.foveationMode === "frontier") {
532
+ const k = f.z.negate(), te = P.x.add(P.w).add(_.y).max(0).mul(1 / 3).sqrt().mul(2), W = _.w, se = W.lessThan(0), ot = W.abs(), wt = ot.equal(0).select(v(zt), ot), at = n.pixelScaleLimit.mul(k), vt = se.select(v(0), te);
533
+ ee = wt.greaterThan(at).and(vt.lessThanEqual(at));
534
+ } else {
535
+ const k = r.maxScreenRadiusPx ?? 0, N = r.minScreenRadiusPx ?? 0, te = Q.sqrt().mul(r.maxStdDev);
536
+ let W = null;
537
+ if (k > 0 && (W = te.lessThanEqual(n.screenBandMax)), N > 0) {
538
+ const se = te.greaterThan(n.screenBandMin);
539
+ W = W ? W.and(se) : se;
540
+ }
541
+ ee = W;
542
+ }
543
+ if (r.performanceProfile === "smooth") {
544
+ const k = st.length(), N = nt.length(), te = u.color.a, W = te.greaterThanEqual(1 / 255).and(k.max(N).mul(2).greaterThanEqual(2)).and(te.mul(k).mul(N).greaterThanEqual(3)), se = u.isotropicCovarianceMix === null ? W : W.or(u.isotropicCovarianceMix.greaterThan(0));
545
+ pe(ee ? se.and(ee) : se, Le);
546
+ } else ee ? pe(ee, Le) : Le();
547
+ }), B;
548
+ })(), e === "display")
549
+ s.fragmentNode = G(() => {
550
+ const b = R.dot(R);
551
+ Ne(b.greaterThan(1));
552
+ let f;
553
+ if (r.lodAlpha && Z && ie) {
554
+ const ue = b.mul(Z.mul(Z).mul(-0.5)).exp(), de = ie.mul(4).sub(3).min(5), Ce = de.mul(de).sub(1).mul(1 / Math.E).exp(), Pe = ue.oneMinus().pow(Ce).oneMinus();
555
+ f = ie.greaterThan(1).select(Pe, ue.mul(ie));
556
+ } else
557
+ f = b.mul(T).exp().mul(x.a);
558
+ const g = f.mul($), B = ve(n.relightMap, Se), p = n.relightSoftness.div(n.viewport.x.max(1)), z = n.relightSoftness.div(n.viewport.y.max(1)), A = B, I = ve(n.relightMap, Se.add(J(p, 0))), P = ve(n.relightMap, Se.add(J(p.negate(), 0))), _ = ve(n.relightMap, Se.add(J(0, z))), ce = ve(n.relightMap, Se.add(J(0, z.negate()))), be = A.a.add(I.a).add(P.a).add(_.a).add(ce.a).max(1e-4), Y = A.rgb.mul(A.a).add(I.rgb.mul(I.a)).add(P.rgb.mul(P.a)).add(_.rgb.mul(_.a)).add(ce.rgb.mul(ce.a)).div(be), le = be.mul(0.2), ye = K(Y, le), Ae = n.relightSoftness.greaterThan(0.5).select(ye, B), Fe = oe(
559
+ S(n.relightBackground),
560
+ Ae.rgb.mul(n.relightBrightness),
561
+ Ae.a
562
+ ), Me = oe(x.rgb, x.rgb.mul(Fe), n.relightBlend);
563
+ return K(Me.mul(g), g);
564
+ })(), s.transparent = !0, s.depthTest = !0, s.depthWrite = !1, s.side = h.DoubleSide, s.blending = h.CustomBlending, s.blendSrc = h.OneFactor, s.blendDst = h.OneMinusSrcAlphaFactor, s.blendSrcAlpha = h.OneFactor, s.blendDstAlpha = h.OneMinusSrcAlphaFactor, s.toneMapped = !1;
565
+ else {
566
+ if (!i)
567
+ throw new Error("applySplatMaterialGraph in 'pick' mode requires pick uniforms.");
568
+ s.fragmentNode = G(() => {
569
+ const b = R.dot(R);
570
+ Ne(b.greaterThan(1));
571
+ const g = b.mul(T).exp().mul(x.a).mul($);
572
+ Ne(g.lessThan(i.alphaThreshold));
573
+ const p = F.sub(i.near).div(i.far.sub(i.near)).clamp(0, 1).mul(16777215), z = p.div(65536).floor(), A = p.mod(65536).div(256).floor(), I = p.mod(256).floor();
574
+ return K(z.div(255), A.div(255), I.div(255), 1);
575
+ })(), s.transparent = !1, s.depthTest = !0, s.depthWrite = !0, s.side = h.DoubleSide, s.blending = h.NoBlending, s.toneMapped = !1;
576
+ }
577
+ }
578
+ function Nt(s, e) {
579
+ const t = s.elements;
580
+ return e * Math.hypot(t[2], t[6], t[10]);
581
+ }
582
+ function qt(s, e) {
583
+ const t = s.backend;
584
+ return !t || t.isWebGPUBackend !== !0 || typeof t.has != "function" || typeof t.get != "function" || !t.has(e) ? !1 : t.get(e).buffer !== void 0;
585
+ }
586
+ function Wt(s) {
587
+ const e = s.array, t = e.byteLength;
588
+ if (t === 0) return 0;
589
+ const o = e.constructor;
590
+ return s.array = new o(0), t;
591
+ }
592
+ class Gt {
593
+ constructor(e) {
594
+ l(this, "pending");
595
+ /** Released attributes and the `version` they carried when released. */
596
+ l(this, "released", /* @__PURE__ */ new Map());
597
+ l(this, "releasedBytesValue", 0);
598
+ l(this, "warnedDrift", !1);
599
+ this.pending = [...e];
600
+ }
601
+ /** Nothing left to release. */
602
+ get settled() {
603
+ return this.pending.length === 0;
604
+ }
605
+ /** Bytes still mirrored on the JS heap. */
606
+ get pendingBytes() {
607
+ let e = 0;
608
+ for (const t of this.pending) e += t.array.byteLength;
609
+ return e;
610
+ }
611
+ /** Bytes freed so far. Diagnostic. */
612
+ get releasedBytes() {
613
+ return this.releasedBytesValue;
614
+ }
615
+ /**
616
+ * Releases every pending mirror three has uploaded, and checks the ones
617
+ * already released for version drift. Idempotent and cheap once settled.
618
+ *
619
+ * @returns Bytes freed by this call.
620
+ */
621
+ release(e) {
622
+ if (this.checkDrift(), this.pending.length === 0) return 0;
623
+ let t = 0;
624
+ for (let o = this.pending.length - 1; o >= 0; o--) {
625
+ const a = this.pending[o];
626
+ qt(e, a) && (t += Wt(a), this.released.set(a, a.version), this.pending.splice(o, 1));
627
+ }
628
+ return this.releasedBytesValue += t, t;
629
+ }
630
+ /**
631
+ * A released attribute whose `version` moved means someone added a CPU write
632
+ * to a buffer this class promised was GPU-only. Three would then upload the
633
+ * zero-length array - a legal no-op - and the data would simply never reach
634
+ * the GPU. Warn once, so that failure is visible rather than a silent wrong
635
+ * render.
636
+ */
637
+ checkDrift() {
638
+ if (!(this.warnedDrift || this.released.size === 0)) {
639
+ for (const [e, t] of this.released)
640
+ if (e.version !== t) {
641
+ this.warnedDrift = !0, kt(
642
+ `StorageMirrorReleaser: storage attribute "${e.name || "unnamed"}" was written from the CPU after its mirror was released; that write will not reach the GPU. Remove it from the releaser's attribute list.`
643
+ );
644
+ return;
645
+ }
646
+ }
647
+ }
648
+ }
649
+ const j = class j {
650
+ constructor(e) {
651
+ l(this, "kind", "counting");
652
+ l(this, "renderer");
653
+ l(this, "clearPass");
654
+ l(this, "histogramPass");
655
+ l(this, "scanBlocksPass");
656
+ l(this, "scanBlockSumsPass");
657
+ l(this, "addBlockSumOffsetsPass");
658
+ l(this, "addOffsetsPass");
659
+ l(this, "scatterPass");
660
+ /** All stages in dependency order, submitted as one WebGPU compute pass. */
661
+ l(this, "sortPasses");
662
+ /** Sorter-owned working buffers, released on {@link dispose}. */
663
+ l(this, "workingAttributes");
664
+ /** Frees the JS mirrors three keeps behind the GPU-only working buffers. */
665
+ l(this, "mirrors");
666
+ /** Set by {@link dispose}; makes a second dispose a no-op. */
667
+ l(this, "disposed", !1);
668
+ /** Row 2 of the model-view matrix; view-space z = row2 · (position, 1). */
669
+ l(this, "viewRow2", fe(new h.Vector4()));
670
+ l(this, "depthMin", fe(0));
671
+ l(this, "depthScale", fe(0));
672
+ /** Active splat count; a float compares exactly for counts < 2²⁴. */
673
+ l(this, "activeCount", fe(0));
674
+ /** Highest bucket index this sort uses; see {@link effectiveBucketCount}. */
675
+ l(this, "bucketMax", fe(0));
676
+ l(this, "viewCenter", new h.Vector3());
677
+ const { renderer: t, capacity: o, centersTexture: a, dataTextureWidth: n, centersBuffer: r, perSource: i } = e, { BUCKET_COUNT: d, BLOCK_SIZE: c } = j;
678
+ if (!a && !r)
679
+ throw new Error("ComputeSorter: provide centersTexture or centersBuffer.");
680
+ if (a && n === void 0)
681
+ throw new Error("ComputeSorter: dataTextureWidth is required with centersTexture.");
682
+ if (r && i)
683
+ throw new Error("ComputeSorter: work-buffer centers are already in world space.");
684
+ this.renderer = t;
685
+ const T = new h.StorageBufferAttribute(new Uint32Array(d), 1), D = new h.StorageBufferAttribute(
686
+ new Uint32Array(d / c),
687
+ 1
688
+ ), y = new h.StorageBufferAttribute(
689
+ new Uint32Array(d / c / c),
690
+ 1
691
+ ), q = new h.StorageBufferAttribute(new Uint32Array(o), 1);
692
+ this.workingAttributes = [
693
+ T,
694
+ D,
695
+ y,
696
+ q
697
+ ], this.mirrors = new Gt(this.workingAttributes);
698
+ const M = ne(T, "uint", d).toAtomic(), m = ne(D, "uint", d / c), w = ne(
699
+ y,
700
+ "uint",
701
+ d / c / c
702
+ ), V = ne(q, "uint", o), X = ne(e.sourceIndexAttribute, "uint", o), xe = ne(e.splatIndexAttribute, "float", o), ae = r ? ne(r, "vec4", o) : null;
703
+ this.clearPass = G(() => {
704
+ qe(M.element(E), C(0));
705
+ })().compute(d, [c]), this.histogramPass = G(() => {
706
+ pe(v(E).lessThan(this.activeCount), () => {
707
+ const u = L(X.element(E)), x = a ? je(
708
+ u.mod(L(n)),
709
+ u.div(L(n))
710
+ ) : null, R = ae ? ae.element(u).xyz : O(a, x).xyz, F = i ? mt(
711
+ i.columns,
712
+ L(O(i.sourceIdTexture, x).r),
713
+ R
714
+ ).worldCenter : R, Z = this.viewRow2.xyz.dot(F).add(this.viewRow2.w).sub(this.depthMin).mul(this.depthScale).clamp(0, this.bucketMax).toUint();
715
+ V.element(E).assign(Z), rt(M.element(Z), C(1));
716
+ });
717
+ })().compute(o, [c]), this.scanBlocksPass = G(() => {
718
+ const u = E.mul(C(c)).toVar(), x = C(0).toVar();
719
+ We(c, ({ i: R }) => {
720
+ const F = M.element(u.add(C(R))), $ = it(F).toVar();
721
+ qe(F, x), x.addAssign($);
722
+ }), m.element(E).assign(x);
723
+ })().compute(d / c, [64]), this.scanBlockSumsPass = G(() => {
724
+ const u = E.mul(C(c)).toVar(), x = C(0).toVar();
725
+ We(c, ({ i: R }) => {
726
+ const F = m.element(u.add(C(R))), $ = F.toVar();
727
+ F.assign(x), x.addAssign($);
728
+ }), w.element(E).assign(x);
729
+ })().compute(d / c / c, [64]);
730
+ const re = G(() => {
731
+ pe(E.equal(C(0)), () => {
732
+ const u = C(0).toVar();
733
+ We(d / c / c, ({ i: x }) => {
734
+ const R = w.element(x).toVar();
735
+ w.element(x).assign(u), u.addAssign(R);
736
+ });
737
+ });
738
+ })().compute(1, [64]);
739
+ this.addBlockSumOffsetsPass = G(() => {
740
+ m.element(E).addAssign(w.element(E.div(C(c))));
741
+ })().compute(d / c, [c]), this.addOffsetsPass = G(() => {
742
+ const u = M.element(E), x = m.element(E.shiftRight(C(Math.log2(c))));
743
+ qe(u, it(u).add(x));
744
+ })().compute(d, [c]), this.scatterPass = G(() => {
745
+ pe(v(E).lessThan(this.activeCount), () => {
746
+ const u = X.element(E), x = V.element(E), R = rt(M.element(x), C(1));
747
+ xe.element(R).assign(v(u));
748
+ });
749
+ })().compute(o, [c]), this.sortPasses = [
750
+ this.clearPass,
751
+ this.histogramPass,
752
+ this.scanBlocksPass,
753
+ this.scanBlockSumsPass,
754
+ re,
755
+ this.addBlockSumOffsetsPass,
756
+ this.addOffsetsPass,
757
+ this.scatterPass
758
+ ];
759
+ }
760
+ /**
761
+ * Buckets to actually use for `activeCount` splats, rounded up to a power of
762
+ * two so block indexing stays exact.
763
+ *
764
+ * The histogram is allocated once for the pool's worst case, but clearing and
765
+ * scanning all 2²² buckets costs the same whether one splat is resident or
766
+ * four million - a fixed per-sort bill that dominates on a mobile GPU when
767
+ * the pool is only partly filled (a streaming scene ramping up, or any small
768
+ * scene). Sizing the dispatch to the live splat count keeps the design's
769
+ * ~1-bucket-per-splat depth resolution, so ordering is unaffected, while
770
+ * skipping work on buckets no splat can land in.
771
+ */
772
+ effectiveBucketCount(e) {
773
+ const o = 2 ** Math.ceil(Math.log2(Math.max(1, e)));
774
+ return Math.min(Math.max(o, j.MIN_BUCKET_COUNT), j.BUCKET_COUNT);
775
+ }
776
+ sort(e, t, o) {
777
+ if (t === 0) return !0;
778
+ const a = e.elements;
779
+ this.viewRow2.value.set(a[2], a[6], a[10], a[14]), this.activeCount.value = t;
780
+ const n = this.effectiveBucketCount(t);
781
+ this.viewCenter.copy(o.center).applyMatrix4(e);
782
+ const r = Nt(e, o.radius), i = this.viewCenter.z - r, d = this.viewCenter.z + r;
783
+ this.depthMin.value = i, this.depthScale.value = (n - 1) / (d - i || 1), this.bucketMax.value = n - 1;
784
+ const c = n / j.BLOCK_SIZE;
785
+ return this.histogramPass.count = t, this.scatterPass.count = t, this.clearPass.count = n, this.scanBlocksPass.count = c, this.scanBlockSumsPass.count = c / j.BLOCK_SIZE, this.addBlockSumOffsetsPass.count = c, this.addOffsetsPass.count = n, this.renderer.compute(this.sortPasses), this.mirrors.settled || this.mirrors.release(this.renderer), !0;
786
+ }
787
+ dispose() {
788
+ if (!this.disposed) {
789
+ this.disposed = !0;
790
+ for (const e of this.sortPasses) e.dispose();
791
+ $t(this.renderer, this.workingAttributes);
792
+ }
793
+ }
794
+ };
795
+ l(j, "BUCKET_COUNT", 1 << 22), l(j, "BLOCK_SIZE", 256), /**
796
+ * Smallest bucket count a sort will dispatch. Below this the fixed passes
797
+ * are already cheap, and the floor keeps every block-scan index exact.
798
+ */
799
+ l(j, "MIN_BUCKET_COUNT", 65536);
800
+ let ut = j;
801
+ function $t(s, e) {
802
+ const t = s._attributes;
803
+ if (t)
804
+ for (const o of e) t.delete(o);
805
+ }
806
+ const jt = 2e6, Ht = 5e6, Kt = 8e6, Xt = 1e-6;
807
+ function Zt(s) {
808
+ if (s !== void 0 && (!Number.isFinite(s) || s < 0))
809
+ throw new RangeError(
810
+ "SplatMesh sortIntervalMs must be a finite number greater than or equal to 0."
811
+ );
812
+ return s;
813
+ }
814
+ function Qt(s, e = !1) {
815
+ return s < jt ? e ? 33 : 0 : s < Ht ? e ? 66 : 50 : s < Kt ? e ? 133 : 100 : 1e3 / 6;
816
+ }
817
+ class xs {
818
+ constructor(e, t = !1) {
819
+ l(this, "sortIntervalMs");
820
+ l(this, "isMobile");
821
+ l(this, "previousModelView", new h.Matrix4());
822
+ l(this, "hasPreviousModelView", !1);
823
+ l(this, "legacyWasMoving", !1);
824
+ l(this, "forcePending", !0);
825
+ l(this, "lastAcceptedAt", -1 / 0);
826
+ l(this, "acceptedCount", 0);
827
+ this.sortIntervalMs = Zt(e), this.isMobile = t;
828
+ }
829
+ /** Forces the next changed or content-invalidated pose to bypass throttling. */
830
+ invalidate() {
831
+ this.forcePending = !0;
832
+ }
833
+ /**
834
+ * Content changes replace pool indices underneath the current draw order.
835
+ * They must bypass cadence so a newly active range never renders through a
836
+ * stale sort while the camera is moving.
837
+ */
838
+ invalidateContent() {
839
+ this.forcePending = !0;
840
+ }
841
+ /**
842
+ * Whether an invalidation is still waiting for an accepted sort. The WebGL2
843
+ * worker path consults this directly: it has no cadence, but a content swap
844
+ * under a stationary camera must still trigger a re-sort - the swap reset
845
+ * the draw list to unsorted active order, and without this the scene would
846
+ * render in that unsorted order until the camera next moved (visible
847
+ * blend-order flicker on every streaming swap).
848
+ */
849
+ hasPendingForce() {
850
+ return this.forcePending;
851
+ }
852
+ /**
853
+ * Returns whether a sort should be submitted at this timestamp.
854
+ */
855
+ shouldSubmit(e, t, o, a) {
856
+ return this.shouldSubmitLegacy(e, t, o, a);
857
+ }
858
+ /**
859
+ * Commits timing state only after the sorter accepts a submission.
860
+ */
861
+ markAccepted(e) {
862
+ this.lastAcceptedAt = e, this.acceptedCount++, this.forcePending = !1;
863
+ }
864
+ /** Internal diagnostics for the demo HUD; it does not alter scheduling. */
865
+ snapshot() {
866
+ return { acceptedCount: this.acceptedCount, lastAcceptedAt: this.lastAcceptedAt };
867
+ }
868
+ shouldSubmitLegacy(e, t, o, a) {
869
+ let n = !1;
870
+ if (this.hasPreviousModelView) {
871
+ const i = Jt(e, this.previousModelView) > Xt;
872
+ n = this.legacyWasMoving && !i, this.legacyWasMoving = i;
873
+ } else
874
+ this.hasPreviousModelView = !0;
875
+ this.previousModelView.copy(e);
876
+ const r = this.sortIntervalMs ?? Qt(o, this.isMobile);
877
+ return this.forcePending ? !0 : e.equals(t) ? !1 : n ? !0 : r === 0 || a - this.lastAcceptedAt >= r;
878
+ }
879
+ }
880
+ function Jt(s, e) {
881
+ let t = 0;
882
+ for (let o = 0; o < 16; o++)
883
+ t = Math.max(t, Math.abs(s.elements[o] - e.elements[o]));
884
+ return t;
885
+ }
886
+ const Ge = new h.Vector2();
887
+ function bs(s, e) {
888
+ var d;
889
+ const t = e.xr;
890
+ if ((t == null ? void 0 : t.isPresenting) !== !0 || typeof t.getCamera != "function") return null;
891
+ t.cameraAutoUpdate !== !1 && typeof t.updateCamera == "function" && t.updateCamera(s);
892
+ const o = t.getCamera(), a = (d = o.cameras) == null ? void 0 : d[0];
893
+ if (!a) return null;
894
+ const n = a.viewport;
895
+ let r = (n == null ? void 0 : n.z) ?? 0, i = (n == null ? void 0 : n.w) ?? 0;
896
+ return (!(r > 0) || !(i > 0)) && (e.getDrawingBufferSize(Ge), r = Ge.x, i = Ge.y), { head: o, eye: a, width: r, height: i };
897
+ }
898
+ function ws(s) {
899
+ return s.isArrayCamera === !0;
900
+ }
901
+ function vs(s, e = {}) {
902
+ const t = s.backend.isWebGPUBackend === !0, o = [...e.requiredFeatures ?? [], ...t ? ["webgpu"] : []], a = e.optionalFeatures ?? [];
903
+ return {
904
+ ...e,
905
+ ...o.length > 0 ? { requiredFeatures: o } : {},
906
+ ...a.length > 0 ? { optionalFeatures: a } : {}
907
+ };
908
+ }
909
+ const Yt = 1, es = 2, ts = 1, ss = 0;
910
+ function Ss(s, e = {
911
+ blend: Yt,
912
+ brightness: es,
913
+ background: ts,
914
+ softness: ss
915
+ }) {
916
+ const t = s.blend !== void 0 ? s.blend : e.blend, o = s.brightness !== void 0 ? s.brightness : e.brightness, a = s.background !== void 0 ? s.background : e.background, n = s.softness !== void 0 ? s.softness : e.softness;
917
+ return {
918
+ blend: Number.isFinite(t) ? Math.min(1, Math.max(0, t)) : e.blend,
919
+ brightness: Number.isFinite(o) ? Math.max(0, o) : e.brightness,
920
+ background: Number.isFinite(a) ? Math.max(0, a) : e.background,
921
+ softness: Number.isFinite(n) ? Math.min(8, Math.max(0, n)) : e.softness
922
+ };
923
+ }
924
+ function Ts() {
925
+ const s = new Uint8Array([255, 255, 255, 255]), e = new h.DataTexture(s, 1, 1);
926
+ return e.needsUpdate = !0, e.magFilter = h.NearestFilter, e.minFilter = h.NearestFilter, e.generateMipmaps = !1, e;
927
+ }
928
+ export {
929
+ gs as A,
930
+ Zt as B,
931
+ ut as C,
932
+ ts as D,
933
+ Ee as E,
934
+ fs as F,
935
+ Bt as G,
936
+ _t as H,
937
+ It as I,
938
+ ls as J,
939
+ ct as M,
940
+ Gt as S,
941
+ xs as W,
942
+ Yt as a,
943
+ es as b,
944
+ ss as c,
945
+ dt as d,
946
+ De as e,
947
+ is as f,
948
+ Ft as g,
949
+ Dt as h,
950
+ ms as i,
951
+ Ss as j,
952
+ us as k,
953
+ ds as l,
954
+ Ts as m,
955
+ cs as n,
956
+ ps as o,
957
+ Rt as p,
958
+ hs as q,
959
+ $t as r,
960
+ Ut as s,
961
+ Vt as t,
962
+ Ot as u,
963
+ Nt as v,
964
+ Lt as w,
965
+ vs as x,
966
+ bs as y,
967
+ ws as z
968
+ };
969
+ //# sourceMappingURL=relighting-CSQF3UcL.js.map