@voluma/vlam 0.3.4 → 0.4.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/dist/core/compute-sorter.d.ts +7 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/radix-sorter.d.ts +6 -0
- package/dist/core/sort-worker-protocol.d.ts +2 -0
- package/dist/core/sorter.d.ts +4 -4
- package/dist/core/splat-mesh-types.d.ts +22 -5
- package/dist/core/splat-mesh.d.ts +11 -2
- package/dist/core/splat-sort-bounds.d.ts +17 -0
- package/dist/core/worker-sorter.d.ts +7 -5
- package/dist/index.js +3 -3
- package/dist/radix-sorter-tSffmerk.js +181 -0
- package/dist/radix-sorter-tSffmerk.js.map +1 -0
- package/dist/relighting-qaANKC4Z.js +997 -0
- package/dist/relighting-qaANKC4Z.js.map +1 -0
- package/dist/{splat-mesh-CsLOQb08.js → splat-mesh-B1KlWHvx.js} +545 -513
- package/dist/splat-mesh-B1KlWHvx.js.map +1 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -1
- package/dist/static-lod.js +1 -1
- package/dist/streaming.js +17 -17
- package/dist/unified.js +17 -17
- package/package.json +1 -1
- package/dist/radix-sorter-BrbUg_CV.js +0 -178
- package/dist/radix-sorter-BrbUg_CV.js.map +0 -1
- package/dist/relighting-Tiwep8yd.js +0 -977
- package/dist/relighting-Tiwep8yd.js.map +0 -1
- package/dist/splat-mesh-CsLOQb08.js.map +0 -1
|
@@ -1,977 +0,0 @@
|
|
|
1
|
-
var Tt = Object.defineProperty;
|
|
2
|
-
var yt = (s, e, t) => e in s ? Tt(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
|
|
3
|
-
var l = (s, e, t) => yt(s, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
-
import * as h from "three/webgpu";
|
|
5
|
-
import { bool as At, mat3 as ye, vec3 as S, float as w, modelViewMatrix as je, vec4 as Q, modelWorldMatrix as Mt, int as N, attribute as Ct, ivec2 as He, textureLoad as _, varying as re, colorSpaceToWorking as Pt, positionGeometry as Ne, Fn as $, cameraProjectionMatrix as kt, If as be, mix as ce, vec2 as te, Discard as qe, texture as Se, screenUV as Te, uniform as xe, uint as P, storage as ie, atomicStore as We, instanceIndex as I, atomicAdd as it, Loop as Ge, atomicLoad as ct } from "three/tsl";
|
|
6
|
-
import { w as Rt } from "./logging-BfPdd7NJ.js";
|
|
7
|
-
function Bt(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: w(1),
|
|
23
|
-
rotation: ye(S(1, 0, 0), S(0, 1, 0), S(0, 0, 1)),
|
|
24
|
-
visible: At(!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() : Mt.mul(Q(t.localCenter, 1)).xyz);
|
|
36
|
-
},
|
|
37
|
-
get viewCenter() {
|
|
38
|
-
return T ?? (T = t.makeViewCenter ? t.makeViewCenter() : je.mul(Q(t.localCenter, 1)).xyz);
|
|
39
|
-
},
|
|
40
|
-
get normal() {
|
|
41
|
-
return D ?? (D = t.makeNormal());
|
|
42
|
-
},
|
|
43
|
-
channel(C) {
|
|
44
|
-
let m = y.get(C);
|
|
45
|
-
return m || (m = t.makeChannel(C), y.set(C, 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 C of s) {
|
|
64
|
-
const m = C(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 Fe = 2048;
|
|
79
|
-
function Et(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 (${Fe} 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 cs {
|
|
108
|
-
constructor(e) {
|
|
109
|
-
l(this, "width", Fe);
|
|
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), Ft(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 = De(a, this.width, this.rows, o), this.colorsTexture = De(
|
|
156
|
-
this.backing.colors,
|
|
157
|
-
this.width,
|
|
158
|
-
this.rows,
|
|
159
|
-
h.UnsignedByteType
|
|
160
|
-
), this.covarianceATexture = De(n, this.width, this.rows, o), this.covarianceBTexture = De(
|
|
161
|
-
this.backing.covarianceB,
|
|
162
|
-
this.width,
|
|
163
|
-
this.rows,
|
|
164
|
-
h.FloatType
|
|
165
|
-
), this.shPackedTextures = this.backing.shPacked.map(
|
|
166
|
-
(r) => Dt(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 Et(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 De(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 Dt(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 ls(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 Ft(s, e, t = Fe) {
|
|
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 us(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 lt = 24, mt = lt * lt;
|
|
327
|
-
function zt(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 ds(s) {
|
|
334
|
-
const e = Math.max(1e-4, s.depth), t = Math.max(1e-4, s.focusDistance), o = zt(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 ?? mt;
|
|
337
|
-
return Math.min(i, d);
|
|
338
|
-
}
|
|
339
|
-
function ms(s, e) {
|
|
340
|
-
return Math.sqrt(Math.max(0, s) / Math.max(1e-9, e));
|
|
341
|
-
}
|
|
342
|
-
function hs(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 ut = 1024, Ut = 1e30, Vt = 0.35 * 0.35;
|
|
347
|
-
function Ot(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), C = d.cross(c), m = c.cross(i), v = q.length(), L = C.length(), J = m.length(), le = v.greaterThanEqual(L).and(v.greaterThanEqual(J)), ue = L.greaterThan(v).and(L.greaterThanEqual(J)), de = le.select(q, ue.select(C, m)), u = de.div(de.length().max(1e-12)), x = u.dot(s.mul(u)), F = T.min(D).min(y).min(x).max(1e-12).mul(o), j = ye(S(F, 0, 0), S(0, F, 0), S(0, 0, F));
|
|
349
|
-
return s.mul(w(1).sub(t)).add(j.mul(t));
|
|
350
|
-
}
|
|
351
|
-
function Lt(s, e, t) {
|
|
352
|
-
const o = s.min(e);
|
|
353
|
-
return {
|
|
354
|
-
lambda1: ce(s, o, t),
|
|
355
|
-
lambda2: ce(e, o, t)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function _t(s, e, t, o, a) {
|
|
359
|
-
const n = o.div(a).pow(2), r = s.min(e).min(n), i = ce(s, r, t), d = ce(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 fs(s) {
|
|
366
|
-
return s;
|
|
367
|
-
}
|
|
368
|
-
function ps() {
|
|
369
|
-
return xe(new h.Vector3());
|
|
370
|
-
}
|
|
371
|
-
function ht(s, e, t) {
|
|
372
|
-
const o = e.mul(N(4)), a = s.element(o), n = s.element(o.add(N(1))), r = s.element(o.add(N(2))), i = s.element(o.add(N(3))), d = a.xyz.mul(t.x).add(n.xyz.mul(t.y)).add(r.xyz.mul(t.z)).add(i.xyz), c = ye(a.xyz, n.xyz, r.xyz);
|
|
373
|
-
return { worldCenter: d, linear: c };
|
|
374
|
-
}
|
|
375
|
-
function Nt(s) {
|
|
376
|
-
return [0, 3, 8, 15][s] ?? 0;
|
|
377
|
-
}
|
|
378
|
-
function ft(s, e, t) {
|
|
379
|
-
if (s.mode === "palette") {
|
|
380
|
-
const n = _(e.covarianceBTexture, t).z.toInt(), r = n.mod(N(64)).mul(N(Nt(s.bands))), i = n.div(N(64));
|
|
381
|
-
return (d) => _(s.paletteTexture, He(r.add(N(d)), i)).xyz;
|
|
382
|
-
}
|
|
383
|
-
const o = s.textures.map(
|
|
384
|
-
(n) => _(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(P(2047)).toFloat().div(2047),
|
|
389
|
-
i.shiftRight(P(11)).bitAnd(P(1023)).toFloat().div(1023),
|
|
390
|
-
i.shiftRight(P(21)).bitAnd(P(2047)).toFloat().div(2047)
|
|
391
|
-
);
|
|
392
|
-
return s.range.min.add(a.mul(d));
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
const we = 0.4886025119029199, X = [
|
|
396
|
-
1.0925484305920792,
|
|
397
|
-
-1.0925484305920792,
|
|
398
|
-
0.31539156525252005,
|
|
399
|
-
-1.0925484305920792,
|
|
400
|
-
0.5462742152960396
|
|
401
|
-
], O = [
|
|
402
|
-
-0.5900435899266435,
|
|
403
|
-
2.890611442640554,
|
|
404
|
-
-0.4570457994644658,
|
|
405
|
-
0.3731763325901154,
|
|
406
|
-
-0.4570457994644658,
|
|
407
|
-
1.445305721320277,
|
|
408
|
-
-0.5900435899266435
|
|
409
|
-
];
|
|
410
|
-
function gs(s, e, t, o) {
|
|
411
|
-
const a = o.x, n = o.y, r = o.z, i = ft(s, e, t), d = i(0).mul(n.mul(-we)).add(i(1).mul(r.mul(we))).add(i(2).mul(a.mul(-we)));
|
|
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(X[0]))).add(i(4).mul(n.mul(r).mul(X[1]))).add(i(5).mul(D.mul(2).sub(c).sub(T).mul(X[2]))).add(i(6).mul(a.mul(r).mul(X[3]))).add(i(7).mul(c.sub(T).mul(X[4])));
|
|
414
|
-
return s.bands === 2 ? y : y.add(i(8).mul(n.mul(c.mul(3).sub(T)).mul(O[0]))).add(i(9).mul(a.mul(n).mul(r).mul(O[1]))).add(i(10).mul(n.mul(D.mul(4).sub(c).sub(T)).mul(O[2]))).add(i(11).mul(r.mul(D.mul(2).sub(c.mul(3)).sub(T.mul(3))).mul(O[3]))).add(i(12).mul(a.mul(D.mul(4).sub(c).sub(T)).mul(O[4]))).add(i(13).mul(r.mul(c.sub(T)).mul(O[5]))).add(i(14).mul(a.mul(c.sub(T.mul(3))).mul(O[6])));
|
|
415
|
-
}
|
|
416
|
-
function xs(s, e, t) {
|
|
417
|
-
const { textures: o, sh: a, uniforms: n, settings: r, pick: i } = t, d = N(Fe), c = w(ut), T = -0.5 * r.maxStdDev * r.maxStdDev, D = Ct("splatIndex", "float").toInt(), y = He(D.mod(d), D.div(d)), q = _(o.colorsTexture, y), C = _(o.centersTexture, y).xyz, m = t.sourcePlacement, v = m ? ht(
|
|
418
|
-
m.columns,
|
|
419
|
-
_(m.sourceIdTexture, y).r.toInt(),
|
|
420
|
-
C
|
|
421
|
-
) : null, L = v ? v.worldCenter : C, J = a === null ? null : (() => {
|
|
422
|
-
const b = v ? v.linear.inverse().mul(L.sub(n.localCameraPosition)).normalize() : L.sub(n.localCameraPosition).normalize(), f = b.x, g = b.y, B = b.z, p = ft(a, o, y), U = p(0).mul(g.mul(-we)).add(p(1).mul(B.mul(we))).add(p(2).mul(f.mul(-we)));
|
|
423
|
-
if (a.bands === 1) return U;
|
|
424
|
-
const A = f.mul(f), E = g.mul(g), M = B.mul(B), z = U.add(p(3).mul(f.mul(g).mul(X[0]))).add(p(4).mul(g.mul(B).mul(X[1]))).add(p(5).mul(M.mul(2).sub(A).sub(E).mul(X[2]))).add(p(6).mul(f.mul(B).mul(X[3]))).add(p(7).mul(A.sub(E).mul(X[4])));
|
|
425
|
-
return a.bands === 2 ? z : z.add(p(8).mul(g.mul(A.mul(3).sub(E)).mul(O[0]))).add(p(9).mul(f.mul(g).mul(B).mul(O[1]))).add(p(10).mul(g.mul(M.mul(4).sub(A).sub(E)).mul(O[2]))).add(
|
|
426
|
-
p(11).mul(
|
|
427
|
-
B.mul(M.mul(2).sub(A.mul(3)).sub(E.mul(3))).mul(O[3])
|
|
428
|
-
)
|
|
429
|
-
).add(p(12).mul(f.mul(M.mul(4).sub(A).sub(E)).mul(O[4]))).add(p(13).mul(B.mul(A.sub(E)).mul(O[5]))).add(p(14).mul(f.mul(A.sub(E.mul(3))).mul(O[6])));
|
|
430
|
-
})(), le = J === null ? q : Q(q.rgb.add(J).clamp(0, 1), q.a), ue = () => {
|
|
431
|
-
const b = _(o.covarianceATexture, y), f = _(o.covarianceBTexture, y), g = ye(
|
|
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 = (v ? v.linear.mul(g).mul(v.linear.transpose()) : g).inverse(), U = n.localCameraPosition.sub(L), A = p.mul(p.mul(U)).normalize();
|
|
436
|
-
return A.mul(A.dot(U).sign());
|
|
437
|
-
}, de = (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 _(f.texture, y).r;
|
|
444
|
-
}, u = Bt(t.modifiers, n.localCameraPosition, {
|
|
445
|
-
index: D,
|
|
446
|
-
localCenter: L,
|
|
447
|
-
sourceCenter: C,
|
|
448
|
-
sourceToLocal: v == null ? void 0 : v.linear,
|
|
449
|
-
color: le,
|
|
450
|
-
makeNormal: ue,
|
|
451
|
-
makeChannel: de
|
|
452
|
-
}), x = re(
|
|
453
|
-
r.srgbOutput ? u.color : Pt(u.color, h.SRGBColorSpace)
|
|
454
|
-
), R = re(Ne.xy), F = e === "pick" ? re(w(0), "pickViewDepth") : null, j = re(w(1), "vOpacityCompensation"), W = r.lodAlpha ? re(w(r.maxStdDev), "vAdjustedStdDev") : null, H = r.lodAlpha ? re(w(1), "vAlpha2") : null, me = r.lodAlpha ? re(w(1), "vVisualOpacity") : null;
|
|
455
|
-
if (s.vertexNode = $(() => {
|
|
456
|
-
const b = u.offset === null ? L : L.add(u.offset), f = je.mul(Q(b, 1)).toVar(), g = kt.mul(f).toVar(), B = Q(0, 0, 2, 1).toVar(), p = e === "pick" ? 1.2 + 2 * ut : 1.2, U = g.w.mul(p), A = g.z.greaterThan(U.negate()).and(g.x.abs().lessThan(U)).and(g.y.abs().lessThan(U)), E = u.visible === null ? A : A.and(u.visible);
|
|
457
|
-
return be(E, () => {
|
|
458
|
-
F && F.assign(f.z.negate());
|
|
459
|
-
const M = _(o.covarianceATexture, y), z = _(o.covarianceBTexture, y), Y = ye(
|
|
460
|
-
S(M.x, M.y, M.z),
|
|
461
|
-
S(M.y, M.w, z.x),
|
|
462
|
-
S(M.z, z.x, z.y)
|
|
463
|
-
), se = v ? v.linear.mul(Y).mul(v.linear.transpose()) : Y;
|
|
464
|
-
let ne = u.rotation === null ? se : u.rotation.mul(se).mul(u.rotation.transpose());
|
|
465
|
-
if (u.isotropicCovarianceMix !== null) {
|
|
466
|
-
const k = u.isotropicVarianceScale ?? w(Vt);
|
|
467
|
-
ne = Ot(
|
|
468
|
-
ne,
|
|
469
|
-
S(0, 0, 1),
|
|
470
|
-
u.isotropicCovarianceMix,
|
|
471
|
-
k
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
const he = w(1).div(f.z), Ae = he.mul(he), Me = S(
|
|
475
|
-
n.focal.x.mul(he),
|
|
476
|
-
0,
|
|
477
|
-
n.focal.x.negate().mul(f.x).mul(Ae)
|
|
478
|
-
), ze = S(
|
|
479
|
-
0,
|
|
480
|
-
n.focal.y.mul(he),
|
|
481
|
-
n.focal.y.negate().mul(f.y).mul(Ae)
|
|
482
|
-
), Ce = je.toMat3().transpose(), fe = Ce.mul(Me), pe = Ce.mul(ze), Pe = fe.dot(ne.mul(fe)), ke = pe.dot(ne.mul(pe)), Ke = fe.dot(ne.mul(pe)), Ue = u.scaleSquared === null ? Pe : Pe.mul(u.scaleSquared), Ve = u.scaleSquared === null ? ke : ke.mul(u.scaleSquared), ve = u.scaleSquared === null ? Ke : Ke.mul(u.scaleSquared), Xe = f.z.negate().max(1e-4), Ze = n.dofFocusDistance.max(1e-4), pt = n.dofAperture.max(0).mul(0.5).div(Ze).atan(), gt = Xe.sub(Ze).abs().div(Xe), xt = n.focal.x.mul(pt.tan()), Qe = gt.mul(xt), Je = Qe.mul(Qe).min(w(mt)), Re = r.projectedFilterProfile === "lcc" ? 0.1 : 0.3, Be = Ue.add(Re).add(Je), Oe = Ve.add(Re).add(Je), Ee = ve, bt = Be.mul(Oe).sub(Ee.mul(Ee)).max(1e-9), Ye = (r.antialias || r.projectedFilterProfile === "lcc" ? Ue.mul(Ve).sub(ve.mul(ve)).max(0) : Ue.add(Re).mul(Ve.add(Re)).sub(ve.mul(ve)).max(1e-9)).div(bt).sqrt();
|
|
483
|
-
j.assign(
|
|
484
|
-
u.isotropicCovarianceMix === null ? Ye : ce(Ye, w(1), u.isotropicCovarianceMix)
|
|
485
|
-
);
|
|
486
|
-
const et = Be.add(Oe).mul(0.5), tt = te(Be.sub(Oe).mul(0.5), Ee).length();
|
|
487
|
-
let ee = et.add(tt), ge = et.sub(tt).max(0);
|
|
488
|
-
if (u.isotropicCovarianceMix !== null) {
|
|
489
|
-
const k = Lt(
|
|
490
|
-
ee,
|
|
491
|
-
ge,
|
|
492
|
-
u.isotropicCovarianceMix
|
|
493
|
-
);
|
|
494
|
-
if (ee = k.lambda1, ge = k.lambda2, u.isotropicScreenRadiusPx !== null) {
|
|
495
|
-
const V = _t(
|
|
496
|
-
ee,
|
|
497
|
-
ge,
|
|
498
|
-
u.isotropicCovarianceMix,
|
|
499
|
-
u.isotropicScreenRadiusPx,
|
|
500
|
-
w(r.maxStdDev)
|
|
501
|
-
);
|
|
502
|
-
ee = V.lambda1, ge = V.lambda2;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
const Le = te(Ee, ee.sub(Be)).add(te(1e-6, 0)).normalize();
|
|
506
|
-
let Ie = w(r.maxStdDev);
|
|
507
|
-
if (r.lodAlpha && W && H && me) {
|
|
508
|
-
const k = le.a, V = k.mul(2), Z = V.mul(4).sub(3).min(5);
|
|
509
|
-
Ie = V.greaterThan(1).select(
|
|
510
|
-
w(r.maxStdDev).add(Z.sub(1).mul(0.7)),
|
|
511
|
-
w(r.maxStdDev)
|
|
512
|
-
), W.assign(Ie), H.assign(V), me.assign(
|
|
513
|
-
k.greaterThan(0).select(u.color.a.div(k.max(1e-8)), w(1))
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
const wt = r.maxAspect && r.maxAspect > 0 ? ee.min(ge.mul(r.maxAspect * r.maxAspect)) : ee, st = w(r.minSplatSizePx ?? 0), nt = Le.mul(
|
|
517
|
-
wt.sqrt().mul(Ie).min(c).max(st)
|
|
518
|
-
), ot = te(Le.y, Le.x.negate()).mul(
|
|
519
|
-
ge.sqrt().mul(Ie).min(c).max(st)
|
|
520
|
-
), _e = () => {
|
|
521
|
-
const k = nt.mul(Ne.x).add(ot.mul(Ne.y)), V = g.xy.div(g.w);
|
|
522
|
-
B.assign(
|
|
523
|
-
Q(
|
|
524
|
-
V.add(k.mul(2).div(n.viewport)),
|
|
525
|
-
g.z.div(g.w),
|
|
526
|
-
1
|
|
527
|
-
)
|
|
528
|
-
);
|
|
529
|
-
};
|
|
530
|
-
let oe;
|
|
531
|
-
if (r.foveationMode === "page-table")
|
|
532
|
-
oe = null;
|
|
533
|
-
else if (r.foveationMode === "frontier") {
|
|
534
|
-
const k = f.z.negate(), Z = M.x.add(M.w).add(z.y).max(0).mul(1 / 3).sqrt().mul(2), G = z.w, ae = G.lessThan(0), at = G.abs(), vt = at.equal(0).select(w(Ut), at), rt = n.pixelScaleLimit.mul(k), St = ae.select(w(0), Z);
|
|
535
|
-
oe = vt.greaterThan(rt).and(St.lessThanEqual(rt));
|
|
536
|
-
} else {
|
|
537
|
-
const k = r.maxScreenRadiusPx ?? 0, V = r.minScreenRadiusPx ?? 0, Z = ee.sqrt().mul(r.maxStdDev);
|
|
538
|
-
let G = null;
|
|
539
|
-
if (k > 0 && (G = Z.lessThanEqual(n.screenBandMax)), V > 0) {
|
|
540
|
-
const ae = Z.greaterThan(n.screenBandMin);
|
|
541
|
-
G = G ? G.and(ae) : ae;
|
|
542
|
-
}
|
|
543
|
-
oe = G;
|
|
544
|
-
}
|
|
545
|
-
if (r.performanceProfile === "smooth") {
|
|
546
|
-
const k = nt.length(), V = ot.length(), Z = u.color.a, G = Z.greaterThanEqual(1 / 255).and(k.max(V).mul(2).greaterThanEqual(2)).and(Z.mul(k).mul(V).greaterThanEqual(3)), ae = u.isotropicCovarianceMix === null ? G : G.or(u.isotropicCovarianceMix.greaterThan(0));
|
|
547
|
-
be(oe ? ae.and(oe) : ae, _e);
|
|
548
|
-
} else oe ? be(oe, _e) : _e();
|
|
549
|
-
}), B;
|
|
550
|
-
})(), e === "display")
|
|
551
|
-
s.fragmentNode = $(() => {
|
|
552
|
-
const b = R.dot(R);
|
|
553
|
-
qe(b.greaterThan(1));
|
|
554
|
-
let f;
|
|
555
|
-
if (r.lodAlpha && W && H && me) {
|
|
556
|
-
const fe = b.mul(W.mul(W).mul(-0.5)).exp(), pe = H.mul(4).sub(3).min(5), Pe = pe.mul(pe).sub(1).mul(1 / Math.E).exp(), ke = fe.oneMinus().pow(Pe).oneMinus();
|
|
557
|
-
f = H.greaterThan(1).select(ke, fe.mul(H)).mul(me);
|
|
558
|
-
} else
|
|
559
|
-
f = b.mul(T).exp().mul(x.a);
|
|
560
|
-
const g = f.mul(j), B = Se(n.relightMap, Te), p = n.relightSoftness.div(n.viewport.x.max(1)), U = n.relightSoftness.div(n.viewport.y.max(1)), A = B, E = Se(n.relightMap, Te.add(te(p, 0))), M = Se(n.relightMap, Te.add(te(p.negate(), 0))), z = Se(n.relightMap, Te.add(te(0, U))), Y = Se(n.relightMap, Te.add(te(0, U.negate()))), se = A.a.add(E.a).add(M.a).add(z.a).add(Y.a).max(1e-4), ne = A.rgb.mul(A.a).add(E.rgb.mul(E.a)).add(M.rgb.mul(M.a)).add(z.rgb.mul(z.a)).add(Y.rgb.mul(Y.a)).div(se), he = se.mul(0.2), Ae = Q(ne, he), Me = n.relightSoftness.greaterThan(0.5).select(Ae, B), ze = ce(
|
|
561
|
-
S(n.relightBackground),
|
|
562
|
-
Me.rgb.mul(n.relightBrightness),
|
|
563
|
-
Me.a
|
|
564
|
-
), Ce = ce(x.rgb, x.rgb.mul(ze), n.relightBlend);
|
|
565
|
-
return Q(Ce.mul(g), g);
|
|
566
|
-
})(), 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;
|
|
567
|
-
else {
|
|
568
|
-
if (!i)
|
|
569
|
-
throw new Error("applySplatMaterialGraph in 'pick' mode requires pick uniforms.");
|
|
570
|
-
s.fragmentNode = $(() => {
|
|
571
|
-
const b = R.dot(R);
|
|
572
|
-
qe(b.greaterThan(1));
|
|
573
|
-
let f;
|
|
574
|
-
if (r.lodAlpha && W && H && me) {
|
|
575
|
-
const M = b.mul(W.mul(W).mul(-0.5)).exp(), z = H.mul(4).sub(3).min(5), Y = z.mul(z).sub(1).mul(1 / Math.E).exp(), se = M.oneMinus().pow(Y).oneMinus();
|
|
576
|
-
f = H.greaterThan(1).select(se, M.mul(H)).mul(me);
|
|
577
|
-
} else
|
|
578
|
-
f = b.mul(T).exp().mul(x.a);
|
|
579
|
-
const g = f.mul(j);
|
|
580
|
-
qe(g.lessThan(i.alphaThreshold));
|
|
581
|
-
const p = F.sub(i.near).div(i.far.sub(i.near)).clamp(0, 1).mul(16777215), U = p.div(65536).floor(), A = p.mod(65536).div(256).floor(), E = p.mod(256).floor();
|
|
582
|
-
return Q(U.div(255), A.div(255), E.div(255), 1);
|
|
583
|
-
})(), s.transparent = !1, s.depthTest = !0, s.depthWrite = !0, s.side = h.DoubleSide, s.blending = h.NoBlending, s.toneMapped = !1;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
function qt(s, e) {
|
|
587
|
-
const t = s.elements;
|
|
588
|
-
return e * Math.hypot(t[2], t[6], t[10]);
|
|
589
|
-
}
|
|
590
|
-
function Wt(s, e) {
|
|
591
|
-
const t = s.backend;
|
|
592
|
-
return !t || t.isWebGPUBackend !== !0 || typeof t.has != "function" || typeof t.get != "function" || !t.has(e) ? !1 : t.get(e).buffer !== void 0;
|
|
593
|
-
}
|
|
594
|
-
function Gt(s) {
|
|
595
|
-
const e = s.array, t = e.byteLength;
|
|
596
|
-
if (t === 0) return 0;
|
|
597
|
-
const o = e.constructor;
|
|
598
|
-
return s.array = new o(0), t;
|
|
599
|
-
}
|
|
600
|
-
class $t {
|
|
601
|
-
constructor(e) {
|
|
602
|
-
l(this, "pending");
|
|
603
|
-
/** Released attributes and the `version` they carried when released. */
|
|
604
|
-
l(this, "released", /* @__PURE__ */ new Map());
|
|
605
|
-
l(this, "releasedBytesValue", 0);
|
|
606
|
-
l(this, "warnedDrift", !1);
|
|
607
|
-
this.pending = [...e];
|
|
608
|
-
}
|
|
609
|
-
/** Nothing left to release. */
|
|
610
|
-
get settled() {
|
|
611
|
-
return this.pending.length === 0;
|
|
612
|
-
}
|
|
613
|
-
/** Bytes still mirrored on the JS heap. */
|
|
614
|
-
get pendingBytes() {
|
|
615
|
-
let e = 0;
|
|
616
|
-
for (const t of this.pending) e += t.array.byteLength;
|
|
617
|
-
return e;
|
|
618
|
-
}
|
|
619
|
-
/** Bytes freed so far. Diagnostic. */
|
|
620
|
-
get releasedBytes() {
|
|
621
|
-
return this.releasedBytesValue;
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Releases every pending mirror three has uploaded, and checks the ones
|
|
625
|
-
* already released for version drift. Idempotent and cheap once settled.
|
|
626
|
-
*
|
|
627
|
-
* @returns Bytes freed by this call.
|
|
628
|
-
*/
|
|
629
|
-
release(e) {
|
|
630
|
-
if (this.checkDrift(), this.pending.length === 0) return 0;
|
|
631
|
-
let t = 0;
|
|
632
|
-
for (let o = this.pending.length - 1; o >= 0; o--) {
|
|
633
|
-
const a = this.pending[o];
|
|
634
|
-
Wt(e, a) && (t += Gt(a), this.released.set(a, a.version), this.pending.splice(o, 1));
|
|
635
|
-
}
|
|
636
|
-
return this.releasedBytesValue += t, t;
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* A released attribute whose `version` moved means someone added a CPU write
|
|
640
|
-
* to a buffer this class promised was GPU-only. Three would then upload the
|
|
641
|
-
* zero-length array - a legal no-op - and the data would simply never reach
|
|
642
|
-
* the GPU. Warn once, so that failure is visible rather than a silent wrong
|
|
643
|
-
* render.
|
|
644
|
-
*/
|
|
645
|
-
checkDrift() {
|
|
646
|
-
if (!(this.warnedDrift || this.released.size === 0)) {
|
|
647
|
-
for (const [e, t] of this.released)
|
|
648
|
-
if (e.version !== t) {
|
|
649
|
-
this.warnedDrift = !0, Rt(
|
|
650
|
-
`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.`
|
|
651
|
-
);
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
const K = class K {
|
|
658
|
-
constructor(e) {
|
|
659
|
-
l(this, "kind", "counting");
|
|
660
|
-
l(this, "renderer");
|
|
661
|
-
l(this, "clearPass");
|
|
662
|
-
l(this, "histogramPass");
|
|
663
|
-
l(this, "scanBlocksPass");
|
|
664
|
-
l(this, "scanBlockSumsPass");
|
|
665
|
-
l(this, "addBlockSumOffsetsPass");
|
|
666
|
-
l(this, "addOffsetsPass");
|
|
667
|
-
l(this, "scatterPass");
|
|
668
|
-
/** All stages in dependency order, submitted as one WebGPU compute pass. */
|
|
669
|
-
l(this, "sortPasses");
|
|
670
|
-
/** Sorter-owned working buffers, released on {@link dispose}. */
|
|
671
|
-
l(this, "workingAttributes");
|
|
672
|
-
/** Frees the JS mirrors three keeps behind the GPU-only working buffers. */
|
|
673
|
-
l(this, "mirrors");
|
|
674
|
-
/** Set by {@link dispose}; makes a second dispose a no-op. */
|
|
675
|
-
l(this, "disposed", !1);
|
|
676
|
-
/** Row 2 of the model-view matrix; view-space z = row2 · (position, 1). */
|
|
677
|
-
l(this, "viewRow2", xe(new h.Vector4()));
|
|
678
|
-
l(this, "depthMin", xe(0));
|
|
679
|
-
l(this, "depthScale", xe(0));
|
|
680
|
-
/** Active splat count; a float compares exactly for counts < 2²⁴. */
|
|
681
|
-
l(this, "activeCount", xe(0));
|
|
682
|
-
/** Highest bucket index this sort uses; see {@link effectiveBucketCount}. */
|
|
683
|
-
l(this, "bucketMax", xe(0));
|
|
684
|
-
l(this, "viewCenter", new h.Vector3());
|
|
685
|
-
const { renderer: t, capacity: o, centersTexture: a, dataTextureWidth: n, centersBuffer: r, perSource: i } = e, { BUCKET_COUNT: d, BLOCK_SIZE: c } = K;
|
|
686
|
-
if (!a && !r)
|
|
687
|
-
throw new Error("ComputeSorter: provide centersTexture or centersBuffer.");
|
|
688
|
-
if (a && n === void 0)
|
|
689
|
-
throw new Error("ComputeSorter: dataTextureWidth is required with centersTexture.");
|
|
690
|
-
if (r && i)
|
|
691
|
-
throw new Error("ComputeSorter: work-buffer centers are already in world space.");
|
|
692
|
-
this.renderer = t;
|
|
693
|
-
const T = new h.StorageBufferAttribute(new Uint32Array(d), 1), D = new h.StorageBufferAttribute(
|
|
694
|
-
new Uint32Array(d / c),
|
|
695
|
-
1
|
|
696
|
-
), y = new h.StorageBufferAttribute(
|
|
697
|
-
new Uint32Array(d / c / c),
|
|
698
|
-
1
|
|
699
|
-
), q = new h.StorageBufferAttribute(new Uint32Array(o), 1);
|
|
700
|
-
this.workingAttributes = [
|
|
701
|
-
T,
|
|
702
|
-
D,
|
|
703
|
-
y,
|
|
704
|
-
q
|
|
705
|
-
], this.mirrors = new $t(this.workingAttributes);
|
|
706
|
-
const C = ie(T, "uint", d).toAtomic(), m = ie(D, "uint", d / c), v = ie(
|
|
707
|
-
y,
|
|
708
|
-
"uint",
|
|
709
|
-
d / c / c
|
|
710
|
-
), L = ie(q, "uint", o), J = ie(e.sourceIndexAttribute, "uint", o), le = ie(e.splatIndexAttribute, "float", o), ue = r ? ie(r, "vec4", o) : null;
|
|
711
|
-
this.clearPass = $(() => {
|
|
712
|
-
We(C.element(I), P(0));
|
|
713
|
-
})().compute(d, [c]), this.histogramPass = $(() => {
|
|
714
|
-
be(w(I).lessThan(this.activeCount), () => {
|
|
715
|
-
const u = N(J.element(I)), x = a ? He(
|
|
716
|
-
u.mod(N(n)),
|
|
717
|
-
u.div(N(n))
|
|
718
|
-
) : null, R = ue ? ue.element(u).xyz : _(a, x).xyz, F = i ? ht(
|
|
719
|
-
i.columns,
|
|
720
|
-
N(_(i.sourceIdTexture, x).r),
|
|
721
|
-
R
|
|
722
|
-
).worldCenter : R, W = this.viewRow2.xyz.dot(F).add(this.viewRow2.w).sub(this.depthMin).mul(this.depthScale).clamp(0, this.bucketMax).toUint();
|
|
723
|
-
L.element(I).assign(W), it(C.element(W), P(1));
|
|
724
|
-
});
|
|
725
|
-
})().compute(o, [c]), this.scanBlocksPass = $(() => {
|
|
726
|
-
const u = I.mul(P(c)).toVar(), x = P(0).toVar();
|
|
727
|
-
Ge(c, ({ i: R }) => {
|
|
728
|
-
const F = C.element(u.add(P(R))), j = ct(F).toVar();
|
|
729
|
-
We(F, x), x.addAssign(j);
|
|
730
|
-
}), m.element(I).assign(x);
|
|
731
|
-
})().compute(d / c, [64]), this.scanBlockSumsPass = $(() => {
|
|
732
|
-
const u = I.mul(P(c)).toVar(), x = P(0).toVar();
|
|
733
|
-
Ge(c, ({ i: R }) => {
|
|
734
|
-
const F = m.element(u.add(P(R))), j = F.toVar();
|
|
735
|
-
F.assign(x), x.addAssign(j);
|
|
736
|
-
}), v.element(I).assign(x);
|
|
737
|
-
})().compute(d / c / c, [64]);
|
|
738
|
-
const de = $(() => {
|
|
739
|
-
be(I.equal(P(0)), () => {
|
|
740
|
-
const u = P(0).toVar();
|
|
741
|
-
Ge(d / c / c, ({ i: x }) => {
|
|
742
|
-
const R = v.element(x).toVar();
|
|
743
|
-
v.element(x).assign(u), u.addAssign(R);
|
|
744
|
-
});
|
|
745
|
-
});
|
|
746
|
-
})().compute(1, [64]);
|
|
747
|
-
this.addBlockSumOffsetsPass = $(() => {
|
|
748
|
-
m.element(I).addAssign(v.element(I.div(P(c))));
|
|
749
|
-
})().compute(d / c, [c]), this.addOffsetsPass = $(() => {
|
|
750
|
-
const u = C.element(I), x = m.element(I.shiftRight(P(Math.log2(c))));
|
|
751
|
-
We(u, ct(u).add(x));
|
|
752
|
-
})().compute(d, [c]), this.scatterPass = $(() => {
|
|
753
|
-
be(w(I).lessThan(this.activeCount), () => {
|
|
754
|
-
const u = J.element(I), x = L.element(I), R = it(C.element(x), P(1));
|
|
755
|
-
le.element(R).assign(w(u));
|
|
756
|
-
});
|
|
757
|
-
})().compute(o, [c]), this.sortPasses = [
|
|
758
|
-
this.clearPass,
|
|
759
|
-
this.histogramPass,
|
|
760
|
-
this.scanBlocksPass,
|
|
761
|
-
this.scanBlockSumsPass,
|
|
762
|
-
de,
|
|
763
|
-
this.addBlockSumOffsetsPass,
|
|
764
|
-
this.addOffsetsPass,
|
|
765
|
-
this.scatterPass
|
|
766
|
-
];
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Buckets to actually use for `activeCount` splats, rounded up to a power of
|
|
770
|
-
* two so block indexing stays exact.
|
|
771
|
-
*
|
|
772
|
-
* The histogram is allocated once for the pool's worst case, but clearing and
|
|
773
|
-
* scanning all 2²² buckets costs the same whether one splat is resident or
|
|
774
|
-
* four million - a fixed per-sort bill that dominates on a mobile GPU when
|
|
775
|
-
* the pool is only partly filled (a streaming scene ramping up, or any small
|
|
776
|
-
* scene). Sizing the dispatch to the live splat count keeps the design's
|
|
777
|
-
* ~1-bucket-per-splat depth resolution, so ordering is unaffected, while
|
|
778
|
-
* skipping work on buckets no splat can land in.
|
|
779
|
-
*/
|
|
780
|
-
effectiveBucketCount(e) {
|
|
781
|
-
const o = 2 ** Math.ceil(Math.log2(Math.max(1, e)));
|
|
782
|
-
return Math.min(Math.max(o, K.MIN_BUCKET_COUNT), K.BUCKET_COUNT);
|
|
783
|
-
}
|
|
784
|
-
sort(e, t, o) {
|
|
785
|
-
if (t === 0) return !0;
|
|
786
|
-
const a = e.elements;
|
|
787
|
-
this.viewRow2.value.set(a[2], a[6], a[10], a[14]), this.activeCount.value = t;
|
|
788
|
-
const n = this.effectiveBucketCount(t);
|
|
789
|
-
this.viewCenter.copy(o.center).applyMatrix4(e);
|
|
790
|
-
const r = qt(e, o.radius), i = this.viewCenter.z - r, d = this.viewCenter.z + r;
|
|
791
|
-
this.depthMin.value = i, this.depthScale.value = (n - 1) / (d - i || 1), this.bucketMax.value = n - 1;
|
|
792
|
-
const c = n / K.BLOCK_SIZE;
|
|
793
|
-
return this.histogramPass.count = t, this.scatterPass.count = t, this.clearPass.count = n, this.scanBlocksPass.count = c, this.scanBlockSumsPass.count = c / K.BLOCK_SIZE, this.addBlockSumOffsetsPass.count = c, this.addOffsetsPass.count = n, this.renderer.compute(this.sortPasses), this.mirrors.settled || this.mirrors.release(this.renderer), !0;
|
|
794
|
-
}
|
|
795
|
-
dispose() {
|
|
796
|
-
if (!this.disposed) {
|
|
797
|
-
this.disposed = !0;
|
|
798
|
-
for (const e of this.sortPasses) e.dispose();
|
|
799
|
-
jt(this.renderer, this.workingAttributes);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
l(K, "BUCKET_COUNT", 1 << 22), l(K, "BLOCK_SIZE", 256), /**
|
|
804
|
-
* Smallest bucket count a sort will dispatch. Below this the fixed passes
|
|
805
|
-
* are already cheap, and the floor keeps every block-scan index exact.
|
|
806
|
-
*/
|
|
807
|
-
l(K, "MIN_BUCKET_COUNT", 65536);
|
|
808
|
-
let dt = K;
|
|
809
|
-
function jt(s, e) {
|
|
810
|
-
const t = s._attributes;
|
|
811
|
-
if (t)
|
|
812
|
-
for (const o of e) t.delete(o);
|
|
813
|
-
}
|
|
814
|
-
const Ht = 2e6, Kt = 5e6, Xt = 8e6, Zt = 1e-6;
|
|
815
|
-
function Qt(s) {
|
|
816
|
-
if (s !== void 0 && (!Number.isFinite(s) || s < 0))
|
|
817
|
-
throw new RangeError(
|
|
818
|
-
"SplatMesh sortIntervalMs must be a finite number greater than or equal to 0."
|
|
819
|
-
);
|
|
820
|
-
return s;
|
|
821
|
-
}
|
|
822
|
-
function Jt(s, e = !1) {
|
|
823
|
-
return s < Ht ? e ? 33 : 0 : s < Kt ? e ? 66 : 50 : s < Xt ? e ? 133 : 100 : 1e3 / 6;
|
|
824
|
-
}
|
|
825
|
-
class bs {
|
|
826
|
-
constructor(e, t = !1) {
|
|
827
|
-
l(this, "sortIntervalMs");
|
|
828
|
-
l(this, "isMobile");
|
|
829
|
-
l(this, "previousModelView", new h.Matrix4());
|
|
830
|
-
l(this, "hasPreviousModelView", !1);
|
|
831
|
-
l(this, "legacyWasMoving", !1);
|
|
832
|
-
l(this, "forcePending", !0);
|
|
833
|
-
l(this, "lastAcceptedAt", -1 / 0);
|
|
834
|
-
l(this, "acceptedCount", 0);
|
|
835
|
-
this.sortIntervalMs = Qt(e), this.isMobile = t;
|
|
836
|
-
}
|
|
837
|
-
/** Forces the next changed or content-invalidated pose to bypass throttling. */
|
|
838
|
-
invalidate() {
|
|
839
|
-
this.forcePending = !0;
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Content changes replace pool indices underneath the current draw order.
|
|
843
|
-
* They must bypass cadence so a newly active range never renders through a
|
|
844
|
-
* stale sort while the camera is moving.
|
|
845
|
-
*/
|
|
846
|
-
invalidateContent() {
|
|
847
|
-
this.forcePending = !0;
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Whether an invalidation is still waiting for an accepted sort. The WebGL2
|
|
851
|
-
* worker path consults this directly: it has no cadence, but a content swap
|
|
852
|
-
* under a stationary camera must still trigger a re-sort - the swap reset
|
|
853
|
-
* the draw list to unsorted active order, and without this the scene would
|
|
854
|
-
* render in that unsorted order until the camera next moved (visible
|
|
855
|
-
* blend-order flicker on every streaming swap).
|
|
856
|
-
*/
|
|
857
|
-
hasPendingForce() {
|
|
858
|
-
return this.forcePending;
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* Returns whether a sort should be submitted at this timestamp.
|
|
862
|
-
*/
|
|
863
|
-
shouldSubmit(e, t, o, a) {
|
|
864
|
-
return this.shouldSubmitLegacy(e, t, o, a);
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Commits timing state only after the sorter accepts a submission.
|
|
868
|
-
*/
|
|
869
|
-
markAccepted(e) {
|
|
870
|
-
this.lastAcceptedAt = e, this.acceptedCount++, this.forcePending = !1;
|
|
871
|
-
}
|
|
872
|
-
/** Internal diagnostics for the demo HUD; it does not alter scheduling. */
|
|
873
|
-
snapshot() {
|
|
874
|
-
return { acceptedCount: this.acceptedCount, lastAcceptedAt: this.lastAcceptedAt };
|
|
875
|
-
}
|
|
876
|
-
shouldSubmitLegacy(e, t, o, a) {
|
|
877
|
-
let n = !1;
|
|
878
|
-
if (this.hasPreviousModelView) {
|
|
879
|
-
const i = Yt(e, this.previousModelView) > Zt;
|
|
880
|
-
n = this.legacyWasMoving && !i, this.legacyWasMoving = i;
|
|
881
|
-
} else
|
|
882
|
-
this.hasPreviousModelView = !0;
|
|
883
|
-
this.previousModelView.copy(e);
|
|
884
|
-
const r = this.sortIntervalMs ?? Jt(o, this.isMobile);
|
|
885
|
-
return this.forcePending ? !0 : e.equals(t) ? !1 : n ? !0 : r === 0 || a - this.lastAcceptedAt >= r;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
function Yt(s, e) {
|
|
889
|
-
let t = 0;
|
|
890
|
-
for (let o = 0; o < 16; o++)
|
|
891
|
-
t = Math.max(t, Math.abs(s.elements[o] - e.elements[o]));
|
|
892
|
-
return t;
|
|
893
|
-
}
|
|
894
|
-
const $e = new h.Vector2();
|
|
895
|
-
function ws(s, e) {
|
|
896
|
-
var d;
|
|
897
|
-
const t = e.xr;
|
|
898
|
-
if ((t == null ? void 0 : t.isPresenting) !== !0 || typeof t.getCamera != "function") return null;
|
|
899
|
-
t.cameraAutoUpdate !== !1 && typeof t.updateCamera == "function" && t.updateCamera(s);
|
|
900
|
-
const o = t.getCamera(), a = (d = o.cameras) == null ? void 0 : d[0];
|
|
901
|
-
if (!a) return null;
|
|
902
|
-
const n = a.viewport;
|
|
903
|
-
let r = (n == null ? void 0 : n.z) ?? 0, i = (n == null ? void 0 : n.w) ?? 0;
|
|
904
|
-
return (!(r > 0) || !(i > 0)) && (e.getDrawingBufferSize($e), r = $e.x, i = $e.y), { head: o, eye: a, width: r, height: i };
|
|
905
|
-
}
|
|
906
|
-
function vs(s) {
|
|
907
|
-
return s.isArrayCamera === !0;
|
|
908
|
-
}
|
|
909
|
-
function Ss(s, e = {}) {
|
|
910
|
-
const t = s.backend.isWebGPUBackend === !0, o = [...e.requiredFeatures ?? [], ...t ? ["webgpu"] : []], a = e.optionalFeatures ?? [];
|
|
911
|
-
return {
|
|
912
|
-
...e,
|
|
913
|
-
...o.length > 0 ? { requiredFeatures: o } : {},
|
|
914
|
-
...a.length > 0 ? { optionalFeatures: a } : {}
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
const es = 1, ts = 2, ss = 1, ns = 0;
|
|
918
|
-
function Ts(s, e = {
|
|
919
|
-
blend: es,
|
|
920
|
-
brightness: ts,
|
|
921
|
-
background: ss,
|
|
922
|
-
softness: ns
|
|
923
|
-
}) {
|
|
924
|
-
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;
|
|
925
|
-
return {
|
|
926
|
-
blend: Number.isFinite(t) ? Math.min(1, Math.max(0, t)) : e.blend,
|
|
927
|
-
brightness: Number.isFinite(o) ? Math.max(0, o) : e.brightness,
|
|
928
|
-
background: Number.isFinite(a) ? Math.max(0, a) : e.background,
|
|
929
|
-
softness: Number.isFinite(n) ? Math.min(8, Math.max(0, n)) : e.softness
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
function ys() {
|
|
933
|
-
const s = new Uint8Array([255, 255, 255, 255]), e = new h.DataTexture(s, 1, 1);
|
|
934
|
-
return e.needsUpdate = !0, e.magFilter = h.NearestFilter, e.minFilter = h.NearestFilter, e.generateMipmaps = !1, e;
|
|
935
|
-
}
|
|
936
|
-
export {
|
|
937
|
-
xs as A,
|
|
938
|
-
Qt as B,
|
|
939
|
-
dt as C,
|
|
940
|
-
ss as D,
|
|
941
|
-
De as E,
|
|
942
|
-
ps as F,
|
|
943
|
-
Et as G,
|
|
944
|
-
Nt as H,
|
|
945
|
-
It as I,
|
|
946
|
-
us as J,
|
|
947
|
-
lt as M,
|
|
948
|
-
$t as S,
|
|
949
|
-
bs as W,
|
|
950
|
-
es as a,
|
|
951
|
-
ts as b,
|
|
952
|
-
ns as c,
|
|
953
|
-
mt as d,
|
|
954
|
-
Fe as e,
|
|
955
|
-
cs as f,
|
|
956
|
-
zt as g,
|
|
957
|
-
Ft as h,
|
|
958
|
-
hs as i,
|
|
959
|
-
Ts as j,
|
|
960
|
-
ds as k,
|
|
961
|
-
ms as l,
|
|
962
|
-
ys as m,
|
|
963
|
-
ls as n,
|
|
964
|
-
gs as o,
|
|
965
|
-
Bt as p,
|
|
966
|
-
fs as q,
|
|
967
|
-
jt as r,
|
|
968
|
-
Vt as s,
|
|
969
|
-
Ot as t,
|
|
970
|
-
Lt as u,
|
|
971
|
-
qt as v,
|
|
972
|
-
_t as w,
|
|
973
|
-
Ss as x,
|
|
974
|
-
ws as y,
|
|
975
|
-
vs as z
|
|
976
|
-
};
|
|
977
|
-
//# sourceMappingURL=relighting-Tiwep8yd.js.map
|