@voluma/vlam 0.3.2 → 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/README.md +2 -2
- package/dist/core/compute-sorter.d.ts +7 -1
- package/dist/core/index.d.ts +2 -2
- 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/formats/lcc.js +276 -277
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/rad/index.d.ts +1 -1
- package/dist/formats/rad/parse-rad.d.ts +1 -1
- package/dist/formats/rad.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lod-scheduler-ChoZyEeq.js +466 -0
- package/dist/lod-scheduler-ChoZyEeq.js.map +1 -0
- 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/lod-scheduler.d.ts +32 -0
- package/dist/streaming/lod-source.d.ts +15 -9
- package/dist/streaming/streamed-splat-mesh.d.ts +38 -22
- package/dist/streaming.js +959 -927
- package/dist/streaming.js.map +1 -1
- package/dist/unified.js +17 -17
- package/package.json +1 -1
- package/dist/lod-scheduler-B0a_uBlv.js +0 -383
- package/dist/lod-scheduler-B0a_uBlv.js.map +0 -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,18 +1,18 @@
|
|
|
1
1
|
var pt = Object.defineProperty;
|
|
2
|
-
var ft = (g, u,
|
|
3
|
-
var o = (g, u,
|
|
2
|
+
var ft = (g, u, t) => u in g ? pt(g, u, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[u] = t;
|
|
3
|
+
var o = (g, u, t) => ft(g, typeof u != "symbol" ? u + "" : u, t);
|
|
4
4
|
import { r as Y, M as gt, a as mt } from "./splat-mesh-types-BZIko-_9.js";
|
|
5
5
|
import * as d from "three/webgpu";
|
|
6
6
|
import { uniform as b } from "three/tsl";
|
|
7
|
-
import {
|
|
7
|
+
import { A as wt, f as xt, n as St, c as vt, D as yt, d as Rt, B as Z, E as bt, F as Tt, W as At, G as K, H as Q, I as J, J as Mt, j as It, k as kt, z as Pt, o as Ct, r as Ut, K as q, L as Dt, C as tt, g as Bt } from "./relighting-qaANKC4Z.js";
|
|
8
8
|
import { l as et, w as O } from "./logging-BfPdd7NJ.js";
|
|
9
|
-
import { d as
|
|
9
|
+
import { d as Et } from "./splat-budget-DuVZspPQ.js";
|
|
10
10
|
import { e as G } from "./half-float-Cs4FDq8Q.js";
|
|
11
|
-
import { a as
|
|
12
|
-
function
|
|
11
|
+
import { a as Wt, r as Lt, n as Ft } from "./sh-pack-D5wAe5gg.js";
|
|
12
|
+
function Vt() {
|
|
13
13
|
return new d.Matrix4().makeRotationX(Math.PI);
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function _t(g) {
|
|
16
16
|
switch (g) {
|
|
17
17
|
case "ply":
|
|
18
18
|
case "splat":
|
|
@@ -20,7 +20,7 @@ function Vt(g) {
|
|
|
20
20
|
case "sog":
|
|
21
21
|
case "rad":
|
|
22
22
|
case "streamed-sog":
|
|
23
|
-
return
|
|
23
|
+
return Vt();
|
|
24
24
|
case "spz":
|
|
25
25
|
case "lcc":
|
|
26
26
|
case "lcc2":
|
|
@@ -28,78 +28,82 @@ function Vt(g) {
|
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const lt = `let
|
|
32
|
-
const
|
|
33
|
-
let
|
|
34
|
-
function
|
|
35
|
-
|
|
31
|
+
const lt = `let h = new Float32Array(0), R = new Float32Array(0);
|
|
32
|
+
const a = new Uint32Array(65536);
|
|
33
|
+
let k = 0, Y = new Uint32Array(0), b = new Float32Array(0), I = new Uint32Array(0), E = new Uint32Array(0), K = new Uint32Array(0);
|
|
34
|
+
function W(n) {
|
|
35
|
+
n <= k || (k = n, Y = new Uint32Array(n), b = new Float32Array(n), I = new Uint32Array(n), E = new Uint32Array(n), K = new Uint32Array(n));
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
const
|
|
39
|
-
let
|
|
40
|
-
for (let t = 0; t < r.length; t += 2)
|
|
41
|
-
|
|
42
|
-
let
|
|
37
|
+
function $(n, r, o, D) {
|
|
38
|
+
const z = n[0], B = n[4], T = n[8], j = n[12], q = n[1], G = n[5], H = n[9], J = n[13], L = n[2], N = n[6], O = n[10], P = n[14];
|
|
39
|
+
let l = 0;
|
|
40
|
+
for (let t = 0; t < r.length; t += 2) l += r[t + 1];
|
|
41
|
+
W(l);
|
|
42
|
+
let p = 1 / 0, F = -1 / 0, X = 0;
|
|
43
43
|
for (let t = 0; t < r.length; t += 2) {
|
|
44
|
-
const
|
|
45
|
-
for (let
|
|
46
|
-
const
|
|
47
|
-
|
|
44
|
+
const A = r[t], s = A + r[t + 1];
|
|
45
|
+
for (let e = A; e < s; e++) {
|
|
46
|
+
const f = e * 4, d = R[e], c = o ? d * 16 : 0, u = h[f], U = h[f + 1], g = h[f + 2], M = o ? o[c] * u + o[c + 4] * U + o[c + 8] * g + o[c + 12] : u, x = o ? o[c + 1] * u + o[c + 5] * U + o[c + 9] * g + o[c + 13] : U, S = o ? o[c + 2] * u + o[c + 6] * U + o[c + 10] * g + o[c + 14] : g, _ = L * M + N * x + O * S + P, i = D === "radial" ? -Math.hypot(
|
|
47
|
+
z * M + B * x + T * S + j,
|
|
48
|
+
q * M + G * x + H * S + J,
|
|
49
|
+
_
|
|
50
|
+
) : _;
|
|
51
|
+
Y[X] = e, b[X] = i, X++, i < p && (p = i), i > F && (F = i);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
|
-
const
|
|
51
|
-
let y =
|
|
52
|
-
for (let t = 0; t <
|
|
53
|
-
I[t] = (
|
|
54
|
-
let
|
|
54
|
+
const Q = 16777215 / (F - p || 1);
|
|
55
|
+
let y = E;
|
|
56
|
+
for (let t = 0; t < l; t++)
|
|
57
|
+
I[t] = (b[t] - p) * Q, y[t] = t;
|
|
58
|
+
let w = K;
|
|
55
59
|
for (let t = 0; t < 24; t += 16) {
|
|
56
|
-
|
|
57
|
-
for (let
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
+
a.fill(0);
|
|
61
|
+
for (let s = 0; s < l; s++) {
|
|
62
|
+
const e = I[y[s]] >>> t & 65535;
|
|
63
|
+
a[e] = a[e] + 1;
|
|
60
64
|
}
|
|
61
|
-
let
|
|
62
|
-
for (let
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
+
let A = 0;
|
|
66
|
+
for (let s = 0; s < 65536; s++) {
|
|
67
|
+
const e = a[s];
|
|
68
|
+
a[s] = A, A += e;
|
|
65
69
|
}
|
|
66
|
-
for (let
|
|
67
|
-
const
|
|
68
|
-
|
|
70
|
+
for (let s = 0; s < l; s++) {
|
|
71
|
+
const e = y[s], f = I[e] >>> t & 65535, d = a[f];
|
|
72
|
+
w[d] = e, a[f] = d + 1;
|
|
69
73
|
}
|
|
70
|
-
[y,
|
|
74
|
+
[y, w] = [w, y];
|
|
71
75
|
}
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
for (let t = 0; t <
|
|
75
|
-
return
|
|
76
|
+
E = y, K = w;
|
|
77
|
+
const Z = new Uint32Array(l);
|
|
78
|
+
for (let t = 0; t < l; t++) Z[t] = Y[y[t]];
|
|
79
|
+
return Z;
|
|
76
80
|
}
|
|
77
|
-
self.onmessage = (
|
|
78
|
-
const r =
|
|
81
|
+
self.onmessage = (n) => {
|
|
82
|
+
const r = n.data;
|
|
79
83
|
if (r.type === "init") {
|
|
80
|
-
|
|
84
|
+
h = new Float32Array(r.capacity * 4), R = new Float32Array(r.capacity);
|
|
81
85
|
return;
|
|
82
86
|
}
|
|
83
87
|
if (r.type === "write") {
|
|
84
|
-
|
|
88
|
+
h.set(r.centers, r.start * 4), r.sourceIds && R.set(r.sourceIds, r.start);
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
87
|
-
const
|
|
88
|
-
self.postMessage(D, [
|
|
91
|
+
const o = $(r.modelView, r.spans, r.matrices, r.sortMetric), D = { type: "order", order: o };
|
|
92
|
+
self.postMessage(D, [o.buffer]);
|
|
89
93
|
};
|
|
90
|
-
//# sourceMappingURL=sort-worker-
|
|
94
|
+
//# sourceMappingURL=sort-worker-MzY_zfJ3.js.map
|
|
91
95
|
`, it = typeof self < "u" && self.Blob && new Blob(["URL.revokeObjectURL(import.meta.url);", lt], { type: "text/javascript;charset=utf-8" });
|
|
92
|
-
function
|
|
96
|
+
function zt(g) {
|
|
93
97
|
let u;
|
|
94
98
|
try {
|
|
95
99
|
if (u = it && (self.URL || self.webkitURL).createObjectURL(it), !u) throw "";
|
|
96
|
-
const
|
|
100
|
+
const t = new Worker(u, {
|
|
97
101
|
type: "module",
|
|
98
102
|
name: g == null ? void 0 : g.name
|
|
99
103
|
});
|
|
100
|
-
return
|
|
104
|
+
return t.addEventListener("error", () => {
|
|
101
105
|
(self.URL || self.webkitURL).revokeObjectURL(u);
|
|
102
|
-
}),
|
|
106
|
+
}), t;
|
|
103
107
|
} catch {
|
|
104
108
|
return new Worker(
|
|
105
109
|
"data:text/javascript;charset=utf-8," + encodeURIComponent(lt),
|
|
@@ -111,11 +115,12 @@ function _t(g) {
|
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
class qt {
|
|
114
|
-
constructor(u) {
|
|
118
|
+
constructor(u, t = "depth") {
|
|
115
119
|
o(this, "kind", "worker");
|
|
116
120
|
o(this, "worker");
|
|
117
121
|
o(this, "splatIndexAttribute");
|
|
118
122
|
o(this, "host");
|
|
123
|
+
o(this, "sortMetric");
|
|
119
124
|
o(this, "inFlight", !1);
|
|
120
125
|
/** Set by {@link dispose}; drops any already-delivered order message. */
|
|
121
126
|
o(this, "disposed", !1);
|
|
@@ -126,23 +131,24 @@ class qt {
|
|
|
126
131
|
o(this, "lastSubmittedAt", -1 / 0);
|
|
127
132
|
o(this, "lastCompletedAt", -1 / 0);
|
|
128
133
|
o(this, "lastLatencyMs", Number.NaN);
|
|
129
|
-
this.host = u, this.splatIndexAttribute = u.splatIndexAttribute, this.worker = new
|
|
130
|
-
this.applyOrder(
|
|
131
|
-
}, this.worker.onerror = (
|
|
132
|
-
et("sort worker error - retrying on a later frame.",
|
|
134
|
+
this.host = u, this.sortMetric = t, this.splatIndexAttribute = u.splatIndexAttribute, this.worker = new zt(), this.worker.onmessage = (i) => {
|
|
135
|
+
this.applyOrder(i.data.order);
|
|
136
|
+
}, this.worker.onerror = (i) => {
|
|
137
|
+
et("sort worker error - retrying on a later frame.", i.message), this.inFlight = !1;
|
|
133
138
|
}, this.worker.onmessageerror = () => {
|
|
134
139
|
et("sort worker message deserialization failed."), this.inFlight = !1;
|
|
135
140
|
};
|
|
136
141
|
const e = { type: "init", capacity: u.capacity };
|
|
137
142
|
this.worker.postMessage(e), this.pushCenters([{ start: 0, count: Math.ceil(u.capacity / u.rowWidth) }]);
|
|
138
143
|
}
|
|
139
|
-
sort(u,
|
|
144
|
+
sort(u, t, e) {
|
|
140
145
|
if (this.disposed || this.inFlight) return !1;
|
|
141
146
|
this.inFlight = !0, this.submittedCount++, this.lastSubmittedAt = performance.now(), this.pushCenters(this.host.takeDirtyRows());
|
|
142
147
|
const i = this.host.getActiveSpans();
|
|
143
148
|
this.sentSpans = i;
|
|
144
149
|
const s = {
|
|
145
150
|
type: "sort",
|
|
151
|
+
sortMetric: this.sortMetric,
|
|
146
152
|
modelView: new Float32Array(u.elements),
|
|
147
153
|
spans: i,
|
|
148
154
|
matrices: this.host.perSource ? new Float32Array(this.host.perSource.matrices) : void 0
|
|
@@ -164,42 +170,42 @@ class qt {
|
|
|
164
170
|
}
|
|
165
171
|
/** Sends written pool rows to keep the worker's centers mirror current. */
|
|
166
172
|
pushCenters(u) {
|
|
167
|
-
var
|
|
168
|
-
const
|
|
173
|
+
var e;
|
|
174
|
+
const t = this.host.rowWidth;
|
|
169
175
|
for (const i of u) {
|
|
170
176
|
const s = this.host.centers.slice(
|
|
171
|
-
i.start *
|
|
172
|
-
(i.start + i.count) *
|
|
173
|
-
), r = i.start *
|
|
174
|
-
this.worker.postMessage(
|
|
177
|
+
i.start * t * 4,
|
|
178
|
+
(i.start + i.count) * t * 4
|
|
179
|
+
), r = i.start * t, n = i.count * t, a = (e = this.host.perSource) == null ? void 0 : e.sourceIds.slice(r, r + n), h = { type: "write", start: r, centers: s, sourceIds: a };
|
|
180
|
+
this.worker.postMessage(h, [s.buffer]);
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
applyOrder(u) {
|
|
178
184
|
var s, r;
|
|
179
185
|
if (this.inFlight = !1, this.completedCount++, this.lastCompletedAt = performance.now(), this.lastLatencyMs = this.lastCompletedAt - this.lastSubmittedAt, this.disposed) return;
|
|
180
|
-
const
|
|
181
|
-
if (this.sentSpans = null, !
|
|
182
|
-
for (let n = 0; n <
|
|
183
|
-
if (
|
|
186
|
+
const t = this.host.getActiveSpans(), e = this.sentSpans;
|
|
187
|
+
if (this.sentSpans = null, !e || e.length !== t.length) return;
|
|
188
|
+
for (let n = 0; n < e.length; n++)
|
|
189
|
+
if (e[n] !== t[n]) return;
|
|
184
190
|
const i = this.splatIndexAttribute.array;
|
|
185
191
|
for (let n = 0; n < u.length; n++)
|
|
186
192
|
i[n] = u[n];
|
|
187
193
|
this.splatIndexAttribute.addUpdateRange(0, u.length), this.splatIndexAttribute.needsUpdate = !0, (r = (s = this.host).onOrderApplied) == null || r.call(s);
|
|
188
194
|
}
|
|
189
195
|
}
|
|
190
|
-
const
|
|
191
|
-
function
|
|
192
|
-
return ((g & 255) << 16 | (u & 255) << 8 |
|
|
196
|
+
const Ot = (1 << 24) - 1, st = new d.Vector2(), V = new d.Raycaster(), rt = new d.Vector3();
|
|
197
|
+
function Gt(g, u, t) {
|
|
198
|
+
return ((g & 255) << 16 | (u & 255) << 8 | t & 255) / Ot;
|
|
193
199
|
}
|
|
194
|
-
function
|
|
195
|
-
return u + g * (
|
|
200
|
+
function Ht(g, u, t) {
|
|
201
|
+
return u + g * (t - u);
|
|
196
202
|
}
|
|
197
|
-
function
|
|
198
|
-
st.set(g, u), V.setFromCamera(st,
|
|
199
|
-
const s =
|
|
203
|
+
function Nt(g, u, t, e, i) {
|
|
204
|
+
st.set(g, u), V.setFromCamera(st, e), rt.copy(V.ray.direction).transformDirection(e.matrixWorldInverse);
|
|
205
|
+
const s = t / -rt.z;
|
|
200
206
|
return i.copy(V.ray.origin).addScaledVector(V.ray.direction, s), { point: i, distance: i.distanceTo(V.ray.origin) };
|
|
201
207
|
}
|
|
202
|
-
class
|
|
208
|
+
class Xt {
|
|
203
209
|
constructor(u) {
|
|
204
210
|
/** Alpha below which a splat is transparent enough to pick through. */
|
|
205
211
|
o(this, "alphaThreshold", b(0.1));
|
|
@@ -222,8 +228,8 @@ class Nt {
|
|
|
222
228
|
get uniforms() {
|
|
223
229
|
return { alphaThreshold: this.alphaThreshold, near: this.near, far: this.far };
|
|
224
230
|
}
|
|
225
|
-
pick(u,
|
|
226
|
-
const s = this.queue.then(() => this.run(u,
|
|
231
|
+
pick(u, t, e, i) {
|
|
232
|
+
const s = this.queue.then(() => this.run(u, t, e, i));
|
|
227
233
|
return this.queue = s.then(
|
|
228
234
|
() => {
|
|
229
235
|
},
|
|
@@ -240,32 +246,32 @@ class Nt {
|
|
|
240
246
|
this.material && (this.material.needsUpdate = !0);
|
|
241
247
|
}
|
|
242
248
|
dispose() {
|
|
243
|
-
var u,
|
|
244
|
-
(u = this.material) == null || u.dispose(), this.material = null, this.scene.clear(), this.proxy = null, (
|
|
249
|
+
var u, t;
|
|
250
|
+
(u = this.material) == null || u.dispose(), this.material = null, this.scene.clear(), this.proxy = null, (t = this.target) == null || t.dispose(), this.target = null, this.pickCamera = null;
|
|
245
251
|
}
|
|
246
|
-
async run(u,
|
|
252
|
+
async run(u, t, e, i) {
|
|
247
253
|
if (this.host.isDisposed() || this.host.getActiveCount() === 0 || u.x < -1 || u.x > 1 || u.y < -1 || u.y > 1) return null;
|
|
248
|
-
if (wt(
|
|
254
|
+
if (wt(t))
|
|
249
255
|
throw new Error(
|
|
250
256
|
"SplatMesh.pick: an XR array camera has no single frustum to pick through. Pass one eye (renderer.xr.getCamera().cameras[i]) or a mono camera, and give `ndc` in that eye’s viewport."
|
|
251
257
|
);
|
|
252
|
-
|
|
258
|
+
t.updateMatrixWorld(!0), this.host.updateWorldMatrix(), this.host.prepare(t, e);
|
|
253
259
|
const s = Math.max(1, Math.floor(this.host.getViewportSize().x)), r = Math.max(1, Math.floor(this.host.getViewportSize().y)), n = Math.floor((u.x * 0.5 + 0.5) * s), a = Math.floor((u.y * 0.5 + 0.5) * r);
|
|
254
260
|
if (n < 0 || a < 0 || n >= s || a >= r) return null;
|
|
255
|
-
this.ensureResources(
|
|
256
|
-
const
|
|
257
|
-
this.cropCameraToPixel(l,
|
|
258
|
-
const f = "near" in
|
|
261
|
+
this.ensureResources(t);
|
|
262
|
+
const h = this.target, c = this.proxy, l = this.pickCamera;
|
|
263
|
+
this.cropCameraToPixel(l, t, s, r, n, a), this.host.setView(l, 1, 1);
|
|
264
|
+
const f = "near" in t && typeof t.near == "number" ? t.near : 0.1, x = "far" in t && typeof t.far == "number" ? t.far : 1e3;
|
|
259
265
|
this.near.value = f, this.far.value = x, this.alphaThreshold.value = (i == null ? void 0 : i.alphaThreshold) !== void 0 ? i.alphaThreshold : 0.1;
|
|
260
|
-
const p = this.host.mesh, w =
|
|
261
|
-
|
|
262
|
-
const S =
|
|
263
|
-
|
|
266
|
+
const p = this.host.mesh, w = e.getRenderTarget(), m = e.getScissorTest();
|
|
267
|
+
e.getClearColor(this.savedClearColor);
|
|
268
|
+
const S = e.getClearAlpha(), A = e.autoClear;
|
|
269
|
+
c.matrix.copy(p.matrixWorld), c.matrixWorld.copy(p.matrixWorld), c.visible = this.host.getPickVisible(), c.layers.mask = p.layers.mask, c.renderOrder = p.renderOrder, h.viewport.set(0, 0, 1, 1), h.scissor.set(0, 0, 1, 1);
|
|
264
270
|
const R = (() => {
|
|
265
271
|
try {
|
|
266
|
-
return
|
|
272
|
+
return e.setRenderTarget(h), e.setScissorTest(!1), e.setClearColor(0, 0), e.autoClear = !0, e.clear(), e.setScissorTest(!0), e.autoClear = !1, e.render(this.scene, l), e.readRenderTargetPixelsAsync(h, 0, 0, 1, 1);
|
|
267
273
|
} finally {
|
|
268
|
-
this.host.setView(
|
|
274
|
+
this.host.setView(t, s, r), e.setRenderTarget(w), e.setScissorTest(m), e.setClearColor(this.savedClearColor, S), e.autoClear = A;
|
|
269
275
|
}
|
|
270
276
|
})();
|
|
271
277
|
let y;
|
|
@@ -278,7 +284,7 @@ class Nt {
|
|
|
278
284
|
if (this.host.isDisposed()) return null;
|
|
279
285
|
const M = y[0], U = y[1], I = y[2];
|
|
280
286
|
if (y[3] === 0) return null;
|
|
281
|
-
const B = Gt(
|
|
287
|
+
const B = Ht(Gt(M, U, I), f, x), { point: E, distance: W } = Nt(u.x, u.y, B, t, this.point);
|
|
282
288
|
return { point: E.clone(), distance: W };
|
|
283
289
|
}
|
|
284
290
|
ensureResources(u) {
|
|
@@ -290,24 +296,24 @@ class Nt {
|
|
|
290
296
|
})), this.material === null && (this.material = new d.NodeMaterial(), this.host.applyPickGraph(this.material), this.proxy = new d.Mesh(this.host.mesh.geometry, this.material), this.proxy.matrixAutoUpdate = !1, this.proxy.frustumCulled = !1, this.scene.add(this.proxy)), (this.pickCamera === null || this.pickCamera.constructor !== u.constructor) && (this.pickCamera = u.clone());
|
|
291
297
|
}
|
|
292
298
|
/** Maps one original framebuffer pixel onto the complete 1×1 pick target. */
|
|
293
|
-
cropCameraToPixel(u,
|
|
294
|
-
u.copy(
|
|
295
|
-
const n = u.projectionMatrix, a = n.elements,
|
|
299
|
+
cropCameraToPixel(u, t, e, i, s, r) {
|
|
300
|
+
u.copy(t);
|
|
301
|
+
const n = u.projectionMatrix, a = n.elements, h = 2 * s + 1 - e, c = 2 * r + 1 - i;
|
|
296
302
|
for (let l = 0; l < 4; l++) {
|
|
297
303
|
const f = a[l * 4 + 3];
|
|
298
|
-
a[l * 4] = a[l * 4] *
|
|
304
|
+
a[l * 4] = a[l * 4] * e - h * f, a[l * 4 + 1] = a[l * 4 + 1] * i - c * f;
|
|
299
305
|
}
|
|
300
306
|
u.projectionMatrixInverse.copy(n).invert();
|
|
301
307
|
}
|
|
302
308
|
}
|
|
303
|
-
const
|
|
304
|
-
class
|
|
309
|
+
const jt = 1024, $t = 1 << 21;
|
|
310
|
+
class Yt {
|
|
305
311
|
/**
|
|
306
312
|
* @param centers - The pool's CPU centers (stride 4: x,y,z,_ per splat).
|
|
307
313
|
* @param poolIndices - The pool indices to index (e.g. the active list).
|
|
308
314
|
* @param count - How many entries of `poolIndices` are valid.
|
|
309
315
|
*/
|
|
310
|
-
constructor(u,
|
|
316
|
+
constructor(u, t, e) {
|
|
311
317
|
/** Populated-region minimum corner (local space). */
|
|
312
318
|
o(this, "min", new Float64Array(3));
|
|
313
319
|
/** Per-axis cell size (local units). */
|
|
@@ -321,13 +327,13 @@ class $t {
|
|
|
321
327
|
/** Pool centers array this grid points into (RGBA stride 4). */
|
|
322
328
|
o(this, "centers");
|
|
323
329
|
o(this, "count");
|
|
324
|
-
if (this.centers = u, this.count =
|
|
330
|
+
if (this.centers = u, this.count = e, e === 0) {
|
|
325
331
|
this.dims.set([1, 1, 1]), this.cellSize.set([1, 1, 1]), this.cellStart = new Int32Array(2), this.items = new Uint32Array(0);
|
|
326
332
|
return;
|
|
327
333
|
}
|
|
328
334
|
const i = [1 / 0, 1 / 0, 1 / 0], s = [-1 / 0, -1 / 0, -1 / 0];
|
|
329
|
-
for (let p = 0; p <
|
|
330
|
-
const w =
|
|
335
|
+
for (let p = 0; p < e; p++) {
|
|
336
|
+
const w = t[p] * 4;
|
|
331
337
|
for (let m = 0; m < 3; m++) {
|
|
332
338
|
const S = u[w + m];
|
|
333
339
|
S < i[m] && (i[m] = S), S > s[m] && (s[m] = S);
|
|
@@ -338,34 +344,34 @@ class $t {
|
|
|
338
344
|
s[1] - i[1],
|
|
339
345
|
s[2] - i[2]
|
|
340
346
|
], n = r.filter((p) => p > 0), a = n.reduce((p, w) => p * w, 1);
|
|
341
|
-
let
|
|
342
|
-
(!(
|
|
343
|
-
const
|
|
347
|
+
let h = n.length > 0 ? Math.pow(a / e, 1 / n.length) : 1;
|
|
348
|
+
(!(h > 0) || !Number.isFinite(h)) && (h = 1);
|
|
349
|
+
const c = (p) => {
|
|
344
350
|
for (let w = 0; w < 3; w++) {
|
|
345
351
|
const m = r[w];
|
|
346
|
-
this.dims[w] = m > 0 ? Math.max(1, Math.min(
|
|
352
|
+
this.dims[w] = m > 0 ? Math.max(1, Math.min(jt, Math.round(m / p) || 1)) : 1, this.cellSize[w] = this.dims[w] > 0 && m / this.dims[w] || 1, this.min[w] = i[w];
|
|
347
353
|
}
|
|
348
354
|
};
|
|
349
|
-
for (h
|
|
350
|
-
|
|
355
|
+
for (c(h); this.dims[0] * this.dims[1] * this.dims[2] > $t; )
|
|
356
|
+
h *= 1.5, c(h);
|
|
351
357
|
const l = this.dims[0] * this.dims[1] * this.dims[2], f = new Int32Array(l + 1);
|
|
352
|
-
for (let p = 0; p <
|
|
358
|
+
for (let p = 0; p < e; p++) f[this.cellOf(t[p]) + 1]++;
|
|
353
359
|
for (let p = 0; p < l; p++) f[p + 1] += f[p];
|
|
354
|
-
this.cellStart = f, this.items = new Uint32Array(
|
|
360
|
+
this.cellStart = f, this.items = new Uint32Array(e);
|
|
355
361
|
const x = Int32Array.from(f.subarray(0, l));
|
|
356
|
-
for (let p = 0; p <
|
|
357
|
-
const w =
|
|
362
|
+
for (let p = 0; p < e; p++) {
|
|
363
|
+
const w = t[p];
|
|
358
364
|
this.items[x[this.cellOf(w)]++] = w;
|
|
359
365
|
}
|
|
360
366
|
}
|
|
361
367
|
/** The cell index of pool splat `p`, clamped into range. */
|
|
362
368
|
cellOf(u) {
|
|
363
|
-
const
|
|
364
|
-
return (this.axisCell(2, this.centers[
|
|
369
|
+
const t = u * 4, e = this.axisCell(0, this.centers[t]), i = this.axisCell(1, this.centers[t + 1]);
|
|
370
|
+
return (this.axisCell(2, this.centers[t + 2]) * this.dims[1] + i) * this.dims[0] + e;
|
|
365
371
|
}
|
|
366
|
-
axisCell(u,
|
|
367
|
-
const
|
|
368
|
-
return Math.max(0, Math.min(this.dims[u] - 1,
|
|
372
|
+
axisCell(u, t) {
|
|
373
|
+
const e = Math.floor((t - this.min[u]) / this.cellSize[u]);
|
|
374
|
+
return Math.max(0, Math.min(this.dims[u] - 1, e));
|
|
369
375
|
}
|
|
370
376
|
/**
|
|
371
377
|
* Visits every indexed splat within `radius` (local units) of the local
|
|
@@ -373,18 +379,18 @@ class $t {
|
|
|
373
379
|
* axis span and each candidate is distance-filtered here, so the callback
|
|
374
380
|
* only ever sees splats truly within the radius.
|
|
375
381
|
*/
|
|
376
|
-
forEachWithin(u,
|
|
382
|
+
forEachWithin(u, t, e, i, s) {
|
|
377
383
|
if (this.count === 0 || i < 0) return;
|
|
378
|
-
const r = i * i, n = [0, 0, 0], a = [0, 0, 0],
|
|
379
|
-
for (let
|
|
380
|
-
const l = Math.ceil(i / this.cellSize[
|
|
381
|
-
n[
|
|
384
|
+
const r = i * i, n = [0, 0, 0], a = [0, 0, 0], h = [u, t, e];
|
|
385
|
+
for (let c = 0; c < 3; c++) {
|
|
386
|
+
const l = Math.ceil(i / this.cellSize[c]), f = this.axisCell(c, h[c]);
|
|
387
|
+
n[c] = Math.max(0, f - l), a[c] = Math.min(this.dims[c] - 1, f + l);
|
|
382
388
|
}
|
|
383
|
-
for (let
|
|
389
|
+
for (let c = n[2]; c <= a[2]; c++)
|
|
384
390
|
for (let l = n[1]; l <= a[1]; l++) {
|
|
385
|
-
const f = (
|
|
391
|
+
const f = (c * this.dims[1] + l) * this.dims[0], x = this.cellStart[f + n[0]], p = this.cellStart[f + a[0] + 1];
|
|
386
392
|
for (let w = x; w < p; w++) {
|
|
387
|
-
const m = this.items[w], S = m * 4, A = this.centers[S] - u, R = this.centers[S + 1] -
|
|
393
|
+
const m = this.items[w], S = m * 4, A = this.centers[S] - u, R = this.centers[S + 1] - t, y = this.centers[S + 2] - e, M = A * A + R * R + y * y;
|
|
388
394
|
M <= r && s(m, M);
|
|
389
395
|
}
|
|
390
396
|
}
|
|
@@ -393,35 +399,35 @@ class $t {
|
|
|
393
399
|
* The nearest indexed splat to the local point within `radius`, or null.
|
|
394
400
|
* Returns its pool index and squared local distance.
|
|
395
401
|
*/
|
|
396
|
-
nearest(u,
|
|
402
|
+
nearest(u, t, e, i) {
|
|
397
403
|
let s = -1, r = 1 / 0;
|
|
398
|
-
return this.forEachWithin(u,
|
|
404
|
+
return this.forEachWithin(u, t, e, i, (n, a) => {
|
|
399
405
|
a < r && (r = a, s = n);
|
|
400
406
|
}), s < 0 ? null : { poolIndex: s, distSq: r };
|
|
401
407
|
}
|
|
402
408
|
}
|
|
403
|
-
const
|
|
404
|
-
constructor(
|
|
409
|
+
const Zt = 4, v = class v extends d.Mesh {
|
|
410
|
+
constructor(t, e = {}) {
|
|
405
411
|
var W, L;
|
|
406
|
-
const i = bt(
|
|
407
|
-
if (
|
|
408
|
-
const
|
|
409
|
-
capacity:
|
|
410
|
-
floatTextures:
|
|
412
|
+
const i = bt(e.sortIntervalMs), s = ((W = Et()) == null ? void 0 : W.isMobile) === !0, r = ht(e.maxStdDev) ?? 3, n = te(e.minSplatSizePx) ?? (s ? 1.5 : 0), a = !("capacity" in t), h = a ? t.count : t.capacity;
|
|
413
|
+
if (h <= 0) throw new Error("SplatMesh capacity must be positive.");
|
|
414
|
+
const c = (e.shBands ?? (Y(e.performanceProfile) === "smooth" ? 0 : gt)) !== 0, l = a ? c ? ((L = t.shPacked) == null ? void 0 : L.bands) ?? 0 : 0 : e.shBands ?? 0, f = e.pool, x = () => new Bt({
|
|
415
|
+
capacity: h,
|
|
416
|
+
floatTextures: e.poolFloatTextures,
|
|
411
417
|
packedShBands: l,
|
|
412
418
|
packedShTextureCount: l === 0 ? 0 : Math.ceil(J(l) / 4),
|
|
413
|
-
...
|
|
419
|
+
...e.maxTextureSize === void 0 ? {} : { maxTextureSize: e.maxTextureSize }
|
|
414
420
|
}), p = f !== void 0 && l !== f.packedShBands;
|
|
415
421
|
p && O(
|
|
416
422
|
`a mesh with shBands ${l} cannot share a pool built for ${f == null ? void 0 : f.packedShBands}; it allocated its own pool instead.`
|
|
417
423
|
);
|
|
418
424
|
const w = f === void 0 || p, m = w ? x() : f, S = Math.min(
|
|
419
425
|
m.capacity,
|
|
420
|
-
Math.ceil(
|
|
421
|
-
), A = m.centersTexture, R = m.colorsTexture, y = m.covarianceATexture, M = m.covarianceBTexture, U = a &&
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
426
|
+
Math.ceil(h / v.DATA_TEXTURE_WIDTH) * v.DATA_TEXTURE_WIDTH
|
|
427
|
+
), A = m.centersTexture, R = m.colorsTexture, y = m.covarianceATexture, M = m.covarianceBTexture, U = a && t.sh && c ? Tt(
|
|
428
|
+
t.sh.palette,
|
|
429
|
+
t.sh.paletteWidth,
|
|
430
|
+
t.sh.paletteHeight,
|
|
425
431
|
d.FloatType
|
|
426
432
|
) : null, I = m.shPackedTextures, D = new d.InstancedBufferGeometry();
|
|
427
433
|
D.setIndex([0, 1, 2, 0, 2, 3]), D.setAttribute(
|
|
@@ -569,7 +575,7 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
569
575
|
o(this, "screenBandMin", b(0));
|
|
570
576
|
o(this, "screenBandMax", b(0));
|
|
571
577
|
/** The pick pass and everything it allocates; see `splat-mesh-picking.ts`. */
|
|
572
|
-
o(this, "picker", new
|
|
578
|
+
o(this, "picker", new Xt({
|
|
573
579
|
mesh: this,
|
|
574
580
|
isDisposed: () => this.disposed,
|
|
575
581
|
getActiveCount: () => this.activeCount,
|
|
@@ -577,12 +583,12 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
577
583
|
getPickVisible: () => this.effectiveVisibility,
|
|
578
584
|
hasSorter: () => this.sorter !== null,
|
|
579
585
|
updateWorldMatrix: () => this.updateWorldMatrix(!0, !1),
|
|
580
|
-
prepare: (
|
|
581
|
-
this.flushPendingUploads(
|
|
586
|
+
prepare: (t, e) => {
|
|
587
|
+
this.flushPendingUploads(e), this.refreshProjectionUniforms(t, e), this.sorter !== null && this.requestSortIfNeeded(t, e);
|
|
582
588
|
},
|
|
583
|
-
setView: (
|
|
584
|
-
applyPickGraph: (
|
|
585
|
-
|
|
589
|
+
setView: (t, e, i) => this.writeViewUniforms(t, e, i),
|
|
590
|
+
applyPickGraph: (t) => Z(
|
|
591
|
+
t,
|
|
586
592
|
"pick",
|
|
587
593
|
this.graphInputs(this.materialInputs.textures, this.materialInputs.sh)
|
|
588
594
|
)
|
|
@@ -595,8 +601,10 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
595
601
|
* storage-attribute GPU buffers that never sat in a geometry. */
|
|
596
602
|
o(this, "lastRenderer", null);
|
|
597
603
|
o(this, "currentModelView", new d.Matrix4());
|
|
604
|
+
/** Pose components that can actually change the selected sort key. */
|
|
605
|
+
o(this, "currentSortState", new d.Matrix4());
|
|
598
606
|
/** Initialized to an impossible matrix so the first frame always sorts. */
|
|
599
|
-
o(this, "
|
|
607
|
+
o(this, "lastSortedState", new d.Matrix4().makeScale(0, 0, 0));
|
|
600
608
|
o(this, "sortScheduler");
|
|
601
609
|
/** One-frame queue-headroom hint used before a staged atomic commit. */
|
|
602
610
|
o(this, "deferSortRequestOnce", !1);
|
|
@@ -605,6 +613,7 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
605
613
|
/** The active list the current depth order was built from. */
|
|
606
614
|
o(this, "sortedActiveListVersion", -1);
|
|
607
615
|
o(this, "sortStrategy");
|
|
616
|
+
o(this, "sortMetric");
|
|
608
617
|
/** Resolved only when `sortStrategy === 'radix'`; see {@link ensureRadixSorter}. */
|
|
609
618
|
o(this, "RadixSorterCtor", null);
|
|
610
619
|
o(this, "radixSorterLoad", null);
|
|
@@ -641,17 +650,17 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
641
650
|
o(this, "orientation");
|
|
642
651
|
/** Set once the pool has been checked against the device texture limit. */
|
|
643
652
|
o(this, "textureLimitChecked", !1);
|
|
644
|
-
if (this.pool = m, this.ownsPool = w, m.register(this), this.sortScheduler = new At(i, s), this.sortStrategy =
|
|
653
|
+
if (this.pool = m, this.ownsPool = w, m.register(this), this.sortScheduler = new At(i, s), this.sortStrategy = e.sortStrategy ?? "counting", this.sortMetric = e.sortMetric ?? "depth", (this.sortStrategy === "radix" || this.sortStrategy === "exact") && this.ensureRadixSorter(), this.performanceProfileValue = Y(e.performanceProfile), this.maxStdDevValue = r, this.minSplatSizePx = n, this.antialias = e.antialias ?? (a ? t.antialias ?? !1 : !1), this.projectedFilterProfile = e.projectedFilterProfile ?? "default", this.srgbOutput = e.srgbOutput ?? !1, this.maxSplatScreenRadius = H(e.maxSplatScreenRadius), this.minSplatScreenRadius = H(e.minSplatScreenRadius), this.screenBandMin.value = this.minSplatScreenRadius, this.screenBandMax.value = this.maxSplatScreenRadius, this.foveationMode = mt(e.foveationMode), this.foveationTargetPx = ie(e.foveationTargetPx), this.foveationDrawBudget = re(e.foveationDrawBudget), this.foveationLimitPx = this.foveationTargetPx, this.maxSplatAspect = H(e.maxSplatAspect), this.lodAlpha = e.lodAlpha ?? !1, this.orientation = e.orientation ?? "y-up", this.splatIndexAttribute = E, this.sourceIndexAttribute = new d.StorageBufferAttribute(new Uint32Array(S), 1), this.dataTextures = m.coreTextures, this.isStatic = a, this.shEnabled = U !== null, this.shRange = { min: K(), max: K() }, this.frustumCulled = !1, U && (this.dataTextures = [...this.dataTextures, U]), I.length > 0 && (this.dataTextures = [...this.dataTextures, ...I]), this.materialInputs = {
|
|
645
654
|
textures: { centersTexture: A, colorsTexture: R, covarianceATexture: y, covarianceBTexture: M },
|
|
646
|
-
sh: a &&
|
|
655
|
+
sh: a && t.sh && U ? { mode: "palette", bands: t.sh.bands, paletteTexture: U } : l !== 0 ? {
|
|
647
656
|
mode: "packed",
|
|
648
657
|
bands: l,
|
|
649
658
|
textures: I,
|
|
650
659
|
range: this.shRange
|
|
651
660
|
} : null
|
|
652
661
|
}, this.buildMaterial(this.materialInputs.textures, this.materialInputs.sh), a) {
|
|
653
|
-
this.staticRange = this.appendRange(
|
|
654
|
-
const C = this.orientation === "y-up" ?
|
|
662
|
+
this.staticRange = this.appendRange(t), this.pendingUploadRows = [], this.poolFloatTextures === "float16" && this.syncHalfFloatPoolImages();
|
|
663
|
+
const C = this.orientation === "y-up" ? _t(t.format) : null;
|
|
655
664
|
C && (this.matrix.copy(C), this.matrix.decompose(this.position, this.quaternion, this.scale), this.matrixWorldNeedsUpdate = !0);
|
|
656
665
|
}
|
|
657
666
|
}
|
|
@@ -677,8 +686,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
677
686
|
get freeRowSpans() {
|
|
678
687
|
return this.pool.freeRowSpans;
|
|
679
688
|
}
|
|
680
|
-
set freeRowSpans(
|
|
681
|
-
this.pool.freeRowSpans =
|
|
689
|
+
set freeRowSpans(t) {
|
|
690
|
+
this.pool.freeRowSpans = t;
|
|
682
691
|
}
|
|
683
692
|
get poolFloatTextures() {
|
|
684
693
|
return this.pool.floatTextures;
|
|
@@ -697,8 +706,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
697
706
|
* asked for - useful for a debug readout.
|
|
698
707
|
*/
|
|
699
708
|
get shBands() {
|
|
700
|
-
var
|
|
701
|
-
return ((
|
|
709
|
+
var t;
|
|
710
|
+
return ((t = this.materialInputs.sh) == null ? void 0 : t.bands) ?? 0;
|
|
702
711
|
}
|
|
703
712
|
/**
|
|
704
713
|
* The contribution-culling profile currently in effect (resolved at
|
|
@@ -716,8 +725,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
716
725
|
* material graph, {@link StreamedSplatMesh.setBudget} returns its clamp.
|
|
717
726
|
* Every mutator has a matching readable property.
|
|
718
727
|
*/
|
|
719
|
-
setPerformanceProfile(
|
|
720
|
-
|
|
728
|
+
setPerformanceProfile(t) {
|
|
729
|
+
t !== this.performanceProfileValue && (this.performanceProfileValue = t, this.buildMaterial(this.materialInputs.textures, this.materialInputs.sh));
|
|
721
730
|
}
|
|
722
731
|
/**
|
|
723
732
|
* Gaussian cutoff radius currently in effect, in standard deviations.
|
|
@@ -733,9 +742,9 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
733
742
|
* view; raising it restores the faint outer tail. Rebuilds the material
|
|
734
743
|
* graph, same convention as {@link setPerformanceProfile}.
|
|
735
744
|
*/
|
|
736
|
-
setMaxStdDev(
|
|
737
|
-
const
|
|
738
|
-
|
|
745
|
+
setMaxStdDev(t) {
|
|
746
|
+
const e = ht(t);
|
|
747
|
+
e === void 0 || e === this.maxStdDevValue || (this.maxStdDevValue = e, this.cachedUnifiedView = null, this.buildMaterial(this.materialInputs.textures, this.materialInputs.sh));
|
|
739
748
|
}
|
|
740
749
|
/**
|
|
741
750
|
* Copies a decoded scene (or chunk) into the pool and activates it.
|
|
@@ -745,40 +754,40 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
745
754
|
* @returns A handle to pass to {@link removeRange}.
|
|
746
755
|
* @throws {Error} when the remaining pool capacity cannot fit the range.
|
|
747
756
|
*/
|
|
748
|
-
appendRange(
|
|
749
|
-
return this.appendRangeWithState(
|
|
757
|
+
appendRange(t) {
|
|
758
|
+
return this.appendRangeWithState(t, !0);
|
|
750
759
|
}
|
|
751
760
|
/** Appends uploaded data without exposing it to draw/sort until activated. */
|
|
752
|
-
appendInactiveRange(
|
|
753
|
-
return this.appendRangeWithState(
|
|
761
|
+
appendInactiveRange(t) {
|
|
762
|
+
return this.appendRangeWithState(t, !1);
|
|
754
763
|
}
|
|
755
764
|
/** Reserves an inactive pool range whose data can be filled over multiple frames. */
|
|
756
|
-
reserveInactiveRange(
|
|
757
|
-
if (!Number.isInteger(
|
|
765
|
+
reserveInactiveRange(t) {
|
|
766
|
+
if (!Number.isInteger(t) || t < 0)
|
|
758
767
|
throw new RangeError("SplatMesh.reserveInactiveRange: count must be a non-negative integer.");
|
|
759
|
-
if (
|
|
768
|
+
if (t === 0) {
|
|
760
769
|
const a = Object.freeze({ count: 0 });
|
|
761
770
|
return this.ranges.set(a, { startRow: 0, rowCount: 0, start: 0, count: 0, active: !1 }), a;
|
|
762
771
|
}
|
|
763
|
-
const
|
|
764
|
-
this.ranges.set(n, { startRow: s, rowCount: i, start: r, count:
|
|
772
|
+
const e = v.DATA_TEXTURE_WIDTH, i = Math.ceil(t / e), s = Q(this.freeRowSpans, i, this.poolRows), r = s * e, n = Object.freeze({ count: t });
|
|
773
|
+
this.ranges.set(n, { startRow: s, rowCount: i, start: r, count: t, active: !1 });
|
|
765
774
|
for (const a of this.channels.values())
|
|
766
|
-
a.backing.fill(a.fill, r, r + i *
|
|
775
|
+
a.backing.fill(a.fill, r, r + i * e), a.pendingRows.push({ start: s, count: i });
|
|
767
776
|
return n;
|
|
768
777
|
}
|
|
769
778
|
/** Writes one contiguous segment of a reserved inactive range. */
|
|
770
|
-
writeInactiveRange(
|
|
771
|
-
const s = this.ranges.get(
|
|
779
|
+
writeInactiveRange(t, e, i) {
|
|
780
|
+
const s = this.ranges.get(t);
|
|
772
781
|
if (!s) throw new Error("SplatMesh.writeInactiveRange: unknown range handle.");
|
|
773
782
|
if (s.active) throw new Error("SplatMesh.writeInactiveRange: range is already active.");
|
|
774
|
-
if (!Number.isInteger(i) || i < 0 || i +
|
|
783
|
+
if (!Number.isInteger(i) || i < 0 || i + e.count > s.count)
|
|
775
784
|
throw new RangeError("SplatMesh.writeInactiveRange: write exceeds the reserved range.");
|
|
776
|
-
if (
|
|
777
|
-
this.warnOnIgnoredSh(
|
|
785
|
+
if (e.count === 0) return;
|
|
786
|
+
this.warnOnIgnoredSh(e, "writeInactiveRange");
|
|
778
787
|
const r = s.start + i;
|
|
779
|
-
this.writeSplatRows(r,
|
|
780
|
-
const n = v.DATA_TEXTURE_WIDTH, a = Math.floor(r / n),
|
|
781
|
-
this.markRowsWritten(a,
|
|
788
|
+
this.writeSplatRows(r, e);
|
|
789
|
+
const n = v.DATA_TEXTURE_WIDTH, a = Math.floor(r / n), h = Math.floor((r + e.count - 1) / n);
|
|
790
|
+
this.markRowsWritten(a, h - a + 1), F.setFromArray(e.positions), this.localBounds.union(F), this.boundsDirty = !0;
|
|
782
791
|
}
|
|
783
792
|
/**
|
|
784
793
|
* Overwrites splats at `offset` within a range **in place** - unlike
|
|
@@ -787,31 +796,31 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
787
796
|
* touched rows for upload; the caller must {@link invalidateSort} once per
|
|
788
797
|
* batch since splat depths changed.
|
|
789
798
|
*/
|
|
790
|
-
overwriteRangeData(
|
|
791
|
-
const s = this.ranges.get(
|
|
799
|
+
overwriteRangeData(t, e, i) {
|
|
800
|
+
const s = this.ranges.get(t);
|
|
792
801
|
if (!s) throw new Error("SplatMesh.overwriteRangeData: unknown range handle.");
|
|
793
|
-
if (!Number.isInteger(i) || i < 0 || i +
|
|
802
|
+
if (!Number.isInteger(i) || i < 0 || i + e.count > s.count)
|
|
794
803
|
throw new RangeError("SplatMesh.overwriteRangeData: write exceeds the range.");
|
|
795
|
-
if (
|
|
796
|
-
this.warnOnIgnoredSh(
|
|
804
|
+
if (e.count === 0) return;
|
|
805
|
+
this.warnOnIgnoredSh(e, "overwriteRangeData");
|
|
797
806
|
const r = s.start + i;
|
|
798
|
-
this.writeSplatRows(r,
|
|
799
|
-
const n = v.DATA_TEXTURE_WIDTH, a = Math.floor(r / n),
|
|
800
|
-
this.markRowsWritten(a,
|
|
807
|
+
this.writeSplatRows(r, e);
|
|
808
|
+
const n = v.DATA_TEXTURE_WIDTH, a = Math.floor(r / n), h = Math.floor((r + e.count - 1) / n);
|
|
809
|
+
this.markRowsWritten(a, h - a + 1), F.setFromArray(e.positions), this.localBounds.union(F), this.boundsDirty = !0, this.contentRevision++;
|
|
801
810
|
}
|
|
802
811
|
/**
|
|
803
812
|
* Zeros splats `[offset, offset + count)` of a range so they draw nothing
|
|
804
813
|
* (zero covariance → degenerate quad, zero color). Used to free frontier slab
|
|
805
814
|
* slots that leave the frontier. Queues the rows for upload.
|
|
806
815
|
*/
|
|
807
|
-
degenerateRange(
|
|
808
|
-
const s = this.ranges.get(
|
|
816
|
+
degenerateRange(t, e, i) {
|
|
817
|
+
const s = this.ranges.get(t);
|
|
809
818
|
if (!s) throw new Error("SplatMesh.degenerateRange: unknown range handle.");
|
|
810
819
|
if (i <= 0) return;
|
|
811
|
-
if (!Number.isInteger(
|
|
820
|
+
if (!Number.isInteger(e) || e < 0 || e + i > s.count)
|
|
812
821
|
throw new RangeError("SplatMesh.degenerateRange: range out of bounds.");
|
|
813
|
-
const r = s.start +
|
|
814
|
-
n.fill(0, r * 4, (r + i) * 4), a.fill(0, r * 4, (r + i) * 4),
|
|
822
|
+
const r = s.start + e, { centers: n, colors: a, covarianceA: h, covarianceB: c } = this.backing;
|
|
823
|
+
n.fill(0, r * 4, (r + i) * 4), a.fill(0, r * 4, (r + i) * 4), h.fill(0, r * 4, (r + i) * 4), c.fill(0, r * 4, (r + i) * 4);
|
|
815
824
|
const l = v.DATA_TEXTURE_WIDTH, f = Math.floor(r / l), x = Math.floor((r + i - 1) / l);
|
|
816
825
|
this.markRowsWritten(f, x - f + 1), this.contentRevision++;
|
|
817
826
|
}
|
|
@@ -820,15 +829,15 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
820
829
|
* Shared by the append and staged-write paths, which differ only in where
|
|
821
830
|
* the rows come from and when they are activated.
|
|
822
831
|
*/
|
|
823
|
-
writeSplatRows(
|
|
832
|
+
writeSplatRows(t, e) {
|
|
824
833
|
const { centers: i, colors: s, covarianceA: r, covarianceB: n } = this.backing;
|
|
825
|
-
s.set(
|
|
826
|
-
const a =
|
|
834
|
+
s.set(e.colors, t * 4);
|
|
835
|
+
const a = e.count, h = e.positions, c = e.covariances, l = this.shEnabled && e.sh ? e.sh.labels : null, f = e.frontierParent ?? null;
|
|
827
836
|
for (let x = 0; x < a; x++) {
|
|
828
|
-
const p = (
|
|
829
|
-
i[p + 0] =
|
|
837
|
+
const p = (t + x) * 4, w = x * 3, m = x * 6;
|
|
838
|
+
i[p + 0] = h[w + 0], i[p + 1] = h[w + 1], i[p + 2] = h[w + 2], r[p + 0] = c[m + 0], r[p + 1] = c[m + 1], r[p + 2] = c[m + 2], r[p + 3] = c[m + 3], n[p + 0] = c[m + 4], n[p + 1] = c[m + 5], n[p + 2] = l ? l[x] : 0, n[p + 3] = f ? f[x] : 0;
|
|
830
839
|
}
|
|
831
|
-
this.writePackedSh(
|
|
840
|
+
this.writePackedSh(t, e);
|
|
832
841
|
}
|
|
833
842
|
/**
|
|
834
843
|
* Scatters a chunk's packed SH words into the per-group pool textures.
|
|
@@ -838,24 +847,24 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
838
847
|
* else's view-dependent color. Zero is not neutral (the range is signed and
|
|
839
848
|
* rarely symmetric), so the code for "0.0" in this scene's range is.
|
|
840
849
|
*/
|
|
841
|
-
writePackedSh(
|
|
850
|
+
writePackedSh(t, e) {
|
|
842
851
|
if (this.packedShBands === 0) return;
|
|
843
|
-
const i = this.backing.shPacked, s = J(this.packedShBands), r =
|
|
852
|
+
const i = this.backing.shPacked, s = J(this.packedShBands), r = e.shPacked;
|
|
844
853
|
if (r && r.bands === this.packedShBands) {
|
|
845
854
|
this.applyShRange(r);
|
|
846
|
-
const a = this.currentShRange(),
|
|
847
|
-
for (let
|
|
855
|
+
const a = this.currentShRange(), h = a !== null && !Wt(r.range, a);
|
|
856
|
+
for (let c = 0; c < e.count; c++)
|
|
848
857
|
for (let l = 0; l < s; l++) {
|
|
849
|
-
const f = r.packed[
|
|
850
|
-
i[l >> 2][(
|
|
858
|
+
const f = r.packed[c * s + l];
|
|
859
|
+
i[l >> 2][(t + c) * 4 + (l & 3)] = h ? Lt(f, r.range, a) : f;
|
|
851
860
|
}
|
|
852
861
|
return;
|
|
853
862
|
}
|
|
854
863
|
this.shRangeSet || (this.wrotePreLockNeutralSh = !0);
|
|
855
864
|
const n = this.neutralShWord();
|
|
856
|
-
for (let a = 0; a <
|
|
857
|
-
for (let
|
|
858
|
-
i[
|
|
865
|
+
for (let a = 0; a < e.count; a++)
|
|
866
|
+
for (let h = 0; h < s; h++)
|
|
867
|
+
i[h >> 2][(t + a) * 4 + (h & 3)] = n;
|
|
859
868
|
}
|
|
860
869
|
/**
|
|
861
870
|
* Adopts the first packed-SH chunk's dequantization range as the scene's one
|
|
@@ -863,40 +872,40 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
863
872
|
* chunk measures its own extent, so later chunks are requantized into this
|
|
864
873
|
* one at write time ({@link writePackedSh}) rather than ignored.
|
|
865
874
|
*/
|
|
866
|
-
applyShRange(
|
|
867
|
-
if (!this.shRangeSet && (this.shRange.min.value.set(
|
|
868
|
-
const
|
|
869
|
-
for (const i of this.backing.shPacked) i.fill(
|
|
875
|
+
applyShRange(t) {
|
|
876
|
+
if (!this.shRangeSet && (this.shRange.min.value.set(t.range.min[0], t.range.min[1], t.range.min[2]), this.shRange.max.value.set(t.range.max[0], t.range.max[1], t.range.max[2]), this.shRangeSet = !0, this.wrotePreLockNeutralSh)) {
|
|
877
|
+
const e = this.neutralShWord();
|
|
878
|
+
for (const i of this.backing.shPacked) i.fill(e);
|
|
870
879
|
this.markRowsWritten(0, this.poolRows), this.wrotePreLockNeutralSh = !1;
|
|
871
880
|
}
|
|
872
881
|
}
|
|
873
882
|
/** The scene's locked packed-SH range as a plain tuple, or null if unset. */
|
|
874
883
|
currentShRange() {
|
|
875
884
|
if (!this.shRangeSet) return null;
|
|
876
|
-
const { min:
|
|
885
|
+
const { min: t, max: e } = this.shRange;
|
|
877
886
|
return {
|
|
878
|
-
min: [
|
|
879
|
-
max: [
|
|
887
|
+
min: [t.value.x, t.value.y, t.value.z],
|
|
888
|
+
max: [e.value.x, e.value.y, e.value.z]
|
|
880
889
|
};
|
|
881
890
|
}
|
|
882
891
|
/** The packed word decoding to 0 in every channel under the scene's range. */
|
|
883
892
|
neutralShWord() {
|
|
884
|
-
const
|
|
885
|
-
return
|
|
893
|
+
const t = this.currentShRange();
|
|
894
|
+
return t ? Ft(t) : 0;
|
|
886
895
|
}
|
|
887
896
|
/** Warns once per call site when a source's SH cannot be stored. */
|
|
888
|
-
warnOnIgnoredSh(
|
|
889
|
-
|
|
890
|
-
`SplatMesh.${
|
|
891
|
-
),
|
|
892
|
-
`SplatMesh.${
|
|
897
|
+
warnOnIgnoredSh(t, e) {
|
|
898
|
+
t.sh && !this.isStatic && O(
|
|
899
|
+
`SplatMesh.${e}: palette shN data on appended ranges is ignored in dynamic-capacity mode (per-file palettes cannot be merged into a shared pool).`
|
|
900
|
+
), t.shPacked && this.packedShBands === 0 && O(
|
|
901
|
+
`SplatMesh.${e}: per-splat SH was supplied but the pool has none allocated ` + (this.isStatic ? "(the static source had no `shPacked` at construction)." : "(construct the mesh with `shBands` to store it).")
|
|
893
902
|
);
|
|
894
903
|
}
|
|
895
904
|
/** Atomically includes or excludes a resident range on the next active-list rebuild. */
|
|
896
|
-
setRangeActive(
|
|
897
|
-
const i = this.ranges.get(
|
|
905
|
+
setRangeActive(t, e) {
|
|
906
|
+
const i = this.ranges.get(t);
|
|
898
907
|
if (!i) throw new Error("SplatMesh.setRangeActive: unknown range handle.");
|
|
899
|
-
i.active !==
|
|
908
|
+
i.active !== e && (i.active = e, e ? this.activateRecord(i) : this.deactivateRecord(i), this.contentRevision++);
|
|
900
909
|
}
|
|
901
910
|
/**
|
|
902
911
|
* Activates only the first `prefix` splats of a range (see
|
|
@@ -905,10 +914,10 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
905
914
|
* work to the *drawn* frontier instead of the whole pool-sized slab. Write
|
|
906
915
|
* APIs (`overwriteRangeData` / `degenerateRange`) still address the full range.
|
|
907
916
|
*/
|
|
908
|
-
setRangeActivePrefix(
|
|
909
|
-
const i = this.ranges.get(
|
|
917
|
+
setRangeActivePrefix(t, e) {
|
|
918
|
+
const i = this.ranges.get(t);
|
|
910
919
|
if (!i) throw new Error("SplatMesh.setRangeActivePrefix: unknown range handle.");
|
|
911
|
-
const s = Math.max(0, Math.min(i.count, Math.floor(
|
|
920
|
+
const s = Math.max(0, Math.min(i.count, Math.floor(e))), r = i.active ? i.activePrefix ?? i.count : 0;
|
|
912
921
|
s !== r && (i.active && (this.deactivateRecord(i), i.active = !1), i.activePrefix = s, s > 0 && (i.active = !0, this.activateRecord(i)), this.contentRevision++);
|
|
913
922
|
}
|
|
914
923
|
/**
|
|
@@ -918,14 +927,14 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
918
927
|
* an arbitrary hierarchy frontier, but the referenced pool rows must remain
|
|
919
928
|
* resident for the lifetime of the mesh.
|
|
920
929
|
*/
|
|
921
|
-
replaceActiveIndices(
|
|
922
|
-
const
|
|
923
|
-
if (
|
|
930
|
+
replaceActiveIndices(t) {
|
|
931
|
+
const e = this.sourceIndexAttribute.array;
|
|
932
|
+
if (t.length > e.length)
|
|
924
933
|
throw new RangeError("SplatMesh.replaceActiveIndices: frontier exceeds pool capacity.");
|
|
925
934
|
this.activeSlotByPoolIndex.fill(4294967295);
|
|
926
|
-
for (let s = 0; s <
|
|
927
|
-
const r =
|
|
928
|
-
if (r >=
|
|
935
|
+
for (let s = 0; s < t.length; s++) {
|
|
936
|
+
const r = t[s];
|
|
937
|
+
if (r >= e.length)
|
|
929
938
|
throw new RangeError(
|
|
930
939
|
"SplatMesh.replaceActiveIndices: frontier contains an invalid pool index."
|
|
931
940
|
);
|
|
@@ -933,50 +942,50 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
933
942
|
throw new Error(
|
|
934
943
|
"SplatMesh.replaceActiveIndices: frontier contains duplicate pool indices."
|
|
935
944
|
);
|
|
936
|
-
|
|
945
|
+
e[s] = r, this.activeSlotByPoolIndex[r] = s;
|
|
937
946
|
}
|
|
938
947
|
const i = this.activeCount;
|
|
939
|
-
this.activeCount =
|
|
948
|
+
this.activeCount = t.length, this.commitActiveListMutation(0, Math.max(i, this.activeCount)), this.queryEpoch++, this.contentRevision++;
|
|
940
949
|
}
|
|
941
950
|
/** Fast identity-frontier variant that avoids allocating a large index array. */
|
|
942
|
-
replaceActivePrefix(
|
|
943
|
-
const
|
|
951
|
+
replaceActivePrefix(t) {
|
|
952
|
+
const e = this.sourceIndexAttribute.array, i = Math.max(0, Math.min(e.length, Math.floor(t)));
|
|
944
953
|
this.activeSlotByPoolIndex.fill(4294967295);
|
|
945
954
|
for (let r = 0; r < i; r++)
|
|
946
|
-
|
|
955
|
+
e[r] = r, this.activeSlotByPoolIndex[r] = r;
|
|
947
956
|
const s = this.activeCount;
|
|
948
957
|
this.activeCount = i, this.commitActiveListMutation(0, Math.max(s, i)), this.queryEpoch++, this.contentRevision++;
|
|
949
958
|
}
|
|
950
959
|
/** Replaces a static mesh's resident cut without increasing its fixed pool allocation. */
|
|
951
|
-
replaceStaticData(
|
|
960
|
+
replaceStaticData(t) {
|
|
952
961
|
if (!this.isStatic || !this.staticRange)
|
|
953
962
|
throw new Error("SplatMesh.replaceStaticData: mesh was not constructed from static data.");
|
|
954
|
-
if (
|
|
963
|
+
if (t.count > this.capacity)
|
|
955
964
|
throw new RangeError("SplatMesh.replaceStaticData: cut exceeds pool capacity.");
|
|
956
|
-
this.overwriteRangeData(this.staticRange,
|
|
965
|
+
this.overwriteRangeData(this.staticRange, t, 0), this.setRangeActivePrefix(this.staticRange, t.count), this.invalidateSort();
|
|
957
966
|
}
|
|
958
|
-
appendRangeWithState(
|
|
959
|
-
if (
|
|
960
|
-
const
|
|
961
|
-
return this.ranges.set(
|
|
967
|
+
appendRangeWithState(t, e) {
|
|
968
|
+
if (t.count === 0) {
|
|
969
|
+
const c = Object.freeze({ count: 0 });
|
|
970
|
+
return this.ranges.set(c, { startRow: 0, rowCount: 0, start: 0, count: 0, active: e }), c;
|
|
962
971
|
}
|
|
963
|
-
const i = v.DATA_TEXTURE_WIDTH, s = Math.ceil(
|
|
964
|
-
this.warnOnIgnoredSh(
|
|
965
|
-
const a = Object.freeze({ count:
|
|
966
|
-
this.ranges.set(a,
|
|
967
|
-
for (const
|
|
968
|
-
|
|
969
|
-
return F.setFromArray(
|
|
972
|
+
const i = v.DATA_TEXTURE_WIDTH, s = Math.ceil(t.count / i), r = Q(this.freeRowSpans, s, this.poolRows), n = r * i;
|
|
973
|
+
this.warnOnIgnoredSh(t, "appendRange"), this.writeSplatRows(n, t);
|
|
974
|
+
const a = Object.freeze({ count: t.count }), h = { startRow: r, rowCount: s, start: n, count: t.count, active: e };
|
|
975
|
+
this.ranges.set(a, h), this.markRowsWritten(r, s);
|
|
976
|
+
for (const c of this.channels.values())
|
|
977
|
+
c.backing.fill(c.fill, n, n + s * i), c.pendingRows.push({ start: r, count: s });
|
|
978
|
+
return F.setFromArray(t.positions), this.localBounds.union(F), this.boundsDirty = !0, e && this.activateRecord(h), this.contentRevision++, a;
|
|
970
979
|
}
|
|
971
980
|
/**
|
|
972
981
|
* Deactivates a previously appended range and frees its pool rows for
|
|
973
982
|
* reuse. The bounding sphere used for depth quantization stays a
|
|
974
983
|
* conservative superset until new ranges grow it again.
|
|
975
984
|
*/
|
|
976
|
-
removeRange(
|
|
977
|
-
const
|
|
978
|
-
if (!
|
|
979
|
-
|
|
985
|
+
removeRange(t) {
|
|
986
|
+
const e = this.ranges.get(t);
|
|
987
|
+
if (!e) throw new Error("SplatMesh.removeRange: unknown range handle.");
|
|
988
|
+
e.active && this.deactivateRecord(e), this.ranges.delete(t), e.rowCount > 0 && (this.freeRowSpans = Mt(this.freeRowSpans, e.startRow, e.rowCount)), this.contentRevision++;
|
|
980
989
|
}
|
|
981
990
|
/** Maximum number of splats the pool can hold (row-aligned internally). */
|
|
982
991
|
get capacity() {
|
|
@@ -994,8 +1003,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
994
1003
|
*/
|
|
995
1004
|
getUnifiedSourceView() {
|
|
996
1005
|
this.updateWorldMatrix(!0, !1), this.refreshSortBounds();
|
|
997
|
-
const
|
|
998
|
-
return
|
|
1006
|
+
const t = this.cachedUnifiedView;
|
|
1007
|
+
return t !== null && t.contentRevision === this.contentRevision && t.graphRevision === this.graphRevision && t.activeCount === this.activeCount && t.sh === this.materialInputs.sh && t.modifiers === this.modifierList && t.hasSourcePlacement === (this.perSourceSort !== null) && t.centersTexture === this.centersTexture && t.colorsTexture === this.materialInputs.textures.colorsTexture && this.cachedUnifiedViewMatrixWorld.equals(this.matrixWorld) && this.cachedUnifiedViewLocalBounds.equals(this.boundingSphereLocal) ? t : (this.cachedUnifiedViewMatrixWorld.copy(this.matrixWorld), this.cachedUnifiedViewLocalBounds.copy(this.boundingSphereLocal), this.cachedUnifiedViewWorldBounds.copy(this.boundingSphereLocal).applyMatrix4(this.matrixWorld), this.cachedUnifiedView = {
|
|
999
1008
|
capacity: this.capacity,
|
|
1000
1009
|
sourceIndex: this.sourceIndexAttribute,
|
|
1001
1010
|
activeCount: this.activeCount,
|
|
@@ -1028,8 +1037,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1028
1037
|
* mirrors this resolved visibility so existing per-source hit testing works.
|
|
1029
1038
|
* Internal consumers should clear the state with `null` before disposing.
|
|
1030
1039
|
*/
|
|
1031
|
-
setUnifiedPickVisibility(
|
|
1032
|
-
this.unifiedPickVisibility =
|
|
1040
|
+
setUnifiedPickVisibility(t) {
|
|
1041
|
+
this.unifiedPickVisibility = t;
|
|
1033
1042
|
}
|
|
1034
1043
|
/**
|
|
1035
1044
|
* The visibility that actually decides whether this mesh's splats reach the
|
|
@@ -1051,9 +1060,9 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1051
1060
|
* falling back to {@link compact}.
|
|
1052
1061
|
*/
|
|
1053
1062
|
get freeSplatCapacity() {
|
|
1054
|
-
let
|
|
1055
|
-
for (const
|
|
1056
|
-
return
|
|
1063
|
+
let t = 0;
|
|
1064
|
+
for (const e of this.freeRowSpans) t += e.count;
|
|
1065
|
+
return t * v.DATA_TEXTURE_WIDTH;
|
|
1057
1066
|
}
|
|
1058
1067
|
/**
|
|
1059
1068
|
* Declares a named per-splat data channel - one extra value per pool slot,
|
|
@@ -1069,19 +1078,19 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1069
1078
|
*
|
|
1070
1079
|
* @throws {Error} if a channel of this name already exists.
|
|
1071
1080
|
*/
|
|
1072
|
-
defineChannel(
|
|
1073
|
-
if (this.channels.has(
|
|
1074
|
-
throw new Error(`SplatMesh.defineChannel: channel "${
|
|
1075
|
-
const i =
|
|
1081
|
+
defineChannel(t, e = {}) {
|
|
1082
|
+
if (this.channels.has(t))
|
|
1083
|
+
throw new Error(`SplatMesh.defineChannel: channel "${t}" already defined.`);
|
|
1084
|
+
const i = e.type ?? "float", s = v.DATA_TEXTURE_WIDTH, r = s * this.poolRows, n = i === "byte" ? new Uint8Array(r) : new Float32Array(r), a = e.fill ?? 0;
|
|
1076
1085
|
a && n.fill(a);
|
|
1077
|
-
const
|
|
1086
|
+
const h = i === "byte" ? d.UnsignedByteType : d.FloatType, c = new d.DataTexture(
|
|
1078
1087
|
n,
|
|
1079
1088
|
s,
|
|
1080
1089
|
this.poolRows,
|
|
1081
1090
|
d.RedFormat,
|
|
1082
|
-
|
|
1091
|
+
h
|
|
1083
1092
|
);
|
|
1084
|
-
|
|
1093
|
+
c.needsUpdate = !0, this.channels.set(t, { type: i, fill: a, backing: n, texture: c, textureType: h, pendingRows: [] });
|
|
1085
1094
|
}
|
|
1086
1095
|
/**
|
|
1087
1096
|
* Writes channel values for a resident range, at `[offset, offset + data.length)`
|
|
@@ -1091,20 +1100,20 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1091
1100
|
*
|
|
1092
1101
|
* @throws {Error} for an unknown channel or range, or a write past the range.
|
|
1093
1102
|
*/
|
|
1094
|
-
writeChannel(
|
|
1095
|
-
const r = this.channels.get(
|
|
1096
|
-
if (!r) throw new Error(`SplatMesh.writeChannel: channel "${
|
|
1097
|
-
const n = this.ranges.get(
|
|
1103
|
+
writeChannel(t, e, i, s = 0) {
|
|
1104
|
+
const r = this.channels.get(e);
|
|
1105
|
+
if (!r) throw new Error(`SplatMesh.writeChannel: channel "${e}" is not defined.`);
|
|
1106
|
+
const n = this.ranges.get(t);
|
|
1098
1107
|
if (!n) throw new Error("SplatMesh.writeChannel: unknown range handle.");
|
|
1099
1108
|
if (s < 0 || s + i.length > n.count)
|
|
1100
1109
|
throw new Error(
|
|
1101
1110
|
`SplatMesh.writeChannel: write of ${i.length} at offset ${s} exceeds range count ${n.count}.`
|
|
1102
1111
|
);
|
|
1103
1112
|
if (i.length === 0) return;
|
|
1104
|
-
const a = v.DATA_TEXTURE_WIDTH,
|
|
1105
|
-
r.backing.set(i,
|
|
1106
|
-
const
|
|
1107
|
-
r.pendingRows.push({ start:
|
|
1113
|
+
const a = v.DATA_TEXTURE_WIDTH, h = n.start + s;
|
|
1114
|
+
r.backing.set(i, h);
|
|
1115
|
+
const c = Math.floor(h / a), l = Math.floor((h + i.length - 1) / a);
|
|
1116
|
+
r.pendingRows.push({ start: c, count: l - c + 1 }), this.contentRevision++;
|
|
1108
1117
|
}
|
|
1109
1118
|
/**
|
|
1110
1119
|
* Effect hooks, folded into the vertex stage in array order - each
|
|
@@ -1117,10 +1126,10 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1117
1126
|
get modifiers() {
|
|
1118
1127
|
return this.modifierList;
|
|
1119
1128
|
}
|
|
1120
|
-
set modifiers(
|
|
1121
|
-
if (
|
|
1129
|
+
set modifiers(t) {
|
|
1130
|
+
if (t.length === this.modifierList.length && t.every((s, r) => s === this.modifierList[r])) return;
|
|
1122
1131
|
const i = this.modifierList;
|
|
1123
|
-
this.modifierList = [...
|
|
1132
|
+
this.modifierList = [...t];
|
|
1124
1133
|
try {
|
|
1125
1134
|
this.rebuildGraph();
|
|
1126
1135
|
} catch (s) {
|
|
@@ -1147,9 +1156,9 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1147
1156
|
* after EWA projection (not the stylized M13 scale modifier). Live uniforms
|
|
1148
1157
|
* - no material rebuild. Pass `aperture: 0` to disable.
|
|
1149
1158
|
*/
|
|
1150
|
-
setDepthOfField(
|
|
1151
|
-
const
|
|
1152
|
-
this.dofFocusDistance.value =
|
|
1159
|
+
setDepthOfField(t) {
|
|
1160
|
+
const e = It(t, this.getDepthOfField());
|
|
1161
|
+
this.dofFocusDistance.value = e.focusDistance, this.dofAperture.value = e.aperture;
|
|
1153
1162
|
}
|
|
1154
1163
|
/**
|
|
1155
1164
|
* PlayCanvas-style proxy-mesh relighting. Multiplies baked splat color in the
|
|
@@ -1160,13 +1169,13 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1160
1169
|
* the map texture identity rebuilds the material once. Does not affect the
|
|
1161
1170
|
* pick pass. Does not invalidate a unified gather cache.
|
|
1162
1171
|
*/
|
|
1163
|
-
setRelighting(
|
|
1164
|
-
if (
|
|
1172
|
+
setRelighting(t) {
|
|
1173
|
+
if (t === null) {
|
|
1165
1174
|
this.relightBlend.value = 0, this.relightMap !== this.relightPlaceholder && (this.relightMap = this.relightPlaceholder, this.rebuildGraph());
|
|
1166
1175
|
return;
|
|
1167
1176
|
}
|
|
1168
|
-
const
|
|
1169
|
-
this.relightBlend.value =
|
|
1177
|
+
const e = kt(t, this.getRelighting());
|
|
1178
|
+
this.relightBlend.value = e.blend, this.relightBrightness.value = e.brightness, this.relightBackground.value = e.background, this.relightSoftness.value = e.softness, t.map !== this.relightMap && (this.relightMap = t.map, this.rebuildGraph());
|
|
1170
1179
|
}
|
|
1171
1180
|
/** Current relight numeric uniforms (`blend === 0` means off). */
|
|
1172
1181
|
getRelighting() {
|
|
@@ -1189,8 +1198,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1189
1198
|
* selects smaller splats, and a band left where it was would cull precisely
|
|
1190
1199
|
* the detail that refinement bought.
|
|
1191
1200
|
*/
|
|
1192
|
-
setScreenRadiusBand(
|
|
1193
|
-
this.screenBandMin.value = Math.max(0,
|
|
1201
|
+
setScreenRadiusBand(t, e) {
|
|
1202
|
+
this.screenBandMin.value = Math.max(0, t), this.screenBandMax.value = Math.max(0, e);
|
|
1194
1203
|
}
|
|
1195
1204
|
/** Current core DoF uniforms (`aperture === 0` means off). */
|
|
1196
1205
|
getDepthOfField() {
|
|
@@ -1219,13 +1228,13 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1219
1228
|
* {@link SplatPoolTenant}: follow one range to its new rows. The pool has
|
|
1220
1229
|
* already moved the splat data; this moves what the mesh keys by pool row.
|
|
1221
1230
|
*/
|
|
1222
|
-
relocatePoolRange(
|
|
1223
|
-
const i =
|
|
1231
|
+
relocatePoolRange(t, e) {
|
|
1232
|
+
const i = t, s = v.DATA_TEXTURE_WIDTH;
|
|
1224
1233
|
for (const r of this.channels.values()) {
|
|
1225
|
-
const n = i.startRow * s, a =
|
|
1226
|
-
r.backing.copyWithin(a, n, n +
|
|
1234
|
+
const n = i.startRow * s, a = e * s, h = i.rowCount * s;
|
|
1235
|
+
r.backing.copyWithin(a, n, n + h), r.pendingRows.push({ start: e, count: i.rowCount });
|
|
1227
1236
|
}
|
|
1228
|
-
i.startRow =
|
|
1237
|
+
i.startRow = e, i.start = e * s, this.markRowsWritten(e, i.rowCount);
|
|
1229
1238
|
}
|
|
1230
1239
|
/** {@link SplatPoolTenant}: rebuild everything keyed by pool index. */
|
|
1231
1240
|
onPoolCompacted() {
|
|
@@ -1236,16 +1245,16 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1236
1245
|
* uniforms and requests a depth re-sort when the camera or the content
|
|
1237
1246
|
* has changed. Call this every frame, before `renderer.render`.
|
|
1238
1247
|
*/
|
|
1239
|
-
update(
|
|
1248
|
+
update(t, e, i = {}) {
|
|
1240
1249
|
if (this.disposed) return;
|
|
1241
|
-
this.lastRenderer =
|
|
1250
|
+
this.lastRenderer = e, this.assertPoolFitsDevice(e), t.updateMatrixWorld(), this.updateWorldMatrix(!0, !1), this.updateTimings.activeListMs = 0, this.updateTimings.uploadMs = 0, this.updateTimings.sortSubmitMs = 0, this.updateTimings.stagingTextureAllocations = 0, this.updateTimings.activeListUpdateRanges = 0;
|
|
1242
1251
|
const s = performance.now();
|
|
1243
|
-
this.flushPendingUploads(
|
|
1244
|
-
const r = Pt(
|
|
1245
|
-
let n =
|
|
1246
|
-
if (r ? (n = r.eye, a = r.head,
|
|
1252
|
+
this.flushPendingUploads(e), this.updateTimings.uploadMs = performance.now() - s;
|
|
1253
|
+
const r = Pt(t, e);
|
|
1254
|
+
let n = t, a = t, h, c;
|
|
1255
|
+
if (r ? (n = r.eye, a = r.head, h = r.width, c = r.height) : (e.getDrawingBufferSize(P), h = P.x, c = P.y), this.adaptFoveationLimit(n, c), this.writeViewUniforms(n, h, c, a), this.updateTimings.activeListUpdateRanges = this.sourceIndexAttribute.updateRanges.length, i.sort !== !1) {
|
|
1247
1256
|
const l = performance.now();
|
|
1248
|
-
this.requestSortIfNeeded(a,
|
|
1257
|
+
this.requestSortIfNeeded(a, e), this.updateTimings.sortSubmitMs = performance.now() - l;
|
|
1249
1258
|
}
|
|
1250
1259
|
}
|
|
1251
1260
|
/** Returns the render-preparation CPU timings for the current update. */
|
|
@@ -1271,14 +1280,14 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1271
1280
|
* 16.7 M splats). If the limit can't be read, the check is skipped rather
|
|
1272
1281
|
* than risk a false rejection.
|
|
1273
1282
|
*/
|
|
1274
|
-
assertPoolFitsDevice(
|
|
1283
|
+
assertPoolFitsDevice(t) {
|
|
1275
1284
|
if (this.textureLimitChecked) return;
|
|
1276
1285
|
this.textureLimitChecked = !0;
|
|
1277
|
-
const
|
|
1278
|
-
if (
|
|
1286
|
+
const e = Ct(t);
|
|
1287
|
+
if (e > 0 && this.poolRows > e) {
|
|
1279
1288
|
const i = v.DATA_TEXTURE_WIDTH;
|
|
1280
1289
|
throw new Error(
|
|
1281
|
-
`SplatMesh: this scene needs a ${i}×${this.poolRows} data texture, but this device caps texture dimensions at ${
|
|
1290
|
+
`SplatMesh: this scene needs a ${i}×${this.poolRows} data texture, but this device caps texture dimensions at ${e} (about ${(e * i).toLocaleString("en-US")} splats). Stream it with StreamedSplatMesh, or reduce the splat count.`
|
|
1282
1291
|
);
|
|
1283
1292
|
}
|
|
1284
1293
|
}
|
|
@@ -1298,8 +1307,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1298
1307
|
* Does not return a persistent splat id - LOD streaming and pool reuse make
|
|
1299
1308
|
* identity unstable across frames.
|
|
1300
1309
|
*/
|
|
1301
|
-
pick(
|
|
1302
|
-
return this.picker.pick(
|
|
1310
|
+
pick(t, e, i, s) {
|
|
1311
|
+
return this.picker.pick(t, e, i, s);
|
|
1303
1312
|
}
|
|
1304
1313
|
/**
|
|
1305
1314
|
* The resident splat center nearest a world point, within `radius` (world
|
|
@@ -1320,15 +1329,15 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1320
1329
|
* in an ancestor transform is not supported. Splats displaced by GPU
|
|
1321
1330
|
* modifiers are queried at their undisplaced CPU positions.
|
|
1322
1331
|
*/
|
|
1323
|
-
queryNearest(
|
|
1324
|
-
if (this.activeCount === 0 || !(
|
|
1325
|
-
this.updateWorldMatrix(!0, !1), T.copy(
|
|
1326
|
-
const i = this.ensureQueryGrid(), s =
|
|
1332
|
+
queryNearest(t, e) {
|
|
1333
|
+
if (this.activeCount === 0 || !(e >= 0)) return null;
|
|
1334
|
+
this.updateWorldMatrix(!0, !1), T.copy(t), this.worldToLocal(T);
|
|
1335
|
+
const i = this.ensureQueryGrid(), s = e / this.queryWorldMinScale(), r = e * e;
|
|
1327
1336
|
let n = -1, a = 1 / 0;
|
|
1328
|
-
return i.forEachWithin(T.x, T.y, T.z, s, (
|
|
1329
|
-
this.splatWorldPosition(
|
|
1330
|
-
const l = k.distanceToSquared(
|
|
1331
|
-
l <= r && l < a && (a = l, n =
|
|
1337
|
+
return i.forEachWithin(T.x, T.y, T.z, s, (c) => {
|
|
1338
|
+
this.splatWorldPosition(c, k);
|
|
1339
|
+
const l = k.distanceToSquared(t);
|
|
1340
|
+
l <= r && l < a && (a = l, n = c);
|
|
1332
1341
|
}), n < 0 ? null : { point: this.splatWorldPosition(n, new d.Vector3()), distance: Math.sqrt(a) };
|
|
1333
1342
|
}
|
|
1334
1343
|
/**
|
|
@@ -1340,19 +1349,19 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1340
1349
|
*
|
|
1341
1350
|
* Like {@link queryNearest}, this searches resident, undisplaced CPU centers.
|
|
1342
1351
|
*/
|
|
1343
|
-
queryRay(
|
|
1344
|
-
if (this.activeCount === 0 || !(
|
|
1352
|
+
queryRay(t, e = 0.025, i = 0.05) {
|
|
1353
|
+
if (this.activeCount === 0 || !(e >= 0) || !(i >= 0) || t.direction.lengthSq() === 0)
|
|
1345
1354
|
return null;
|
|
1346
1355
|
this.updateWorldMatrix(!0, !1);
|
|
1347
1356
|
const s = this.sourceIndexAttribute.array.subarray(0, this.activeCount);
|
|
1348
1357
|
let r = -1, n = 1 / 0;
|
|
1349
1358
|
for (const a of s) {
|
|
1350
|
-
this.splatWorldPosition(a, k), T.subVectors(k,
|
|
1351
|
-
const
|
|
1352
|
-
if (
|
|
1353
|
-
T.addScaledVector(
|
|
1354
|
-
const
|
|
1355
|
-
T.lengthSq() <=
|
|
1359
|
+
this.splatWorldPosition(a, k), T.subVectors(k, t.origin);
|
|
1360
|
+
const h = T.dot(t.direction);
|
|
1361
|
+
if (h < 0 || h >= n) continue;
|
|
1362
|
+
T.addScaledVector(t.direction, -h);
|
|
1363
|
+
const c = Math.max(i, h * e);
|
|
1364
|
+
T.lengthSq() <= c * c && (r = a, n = h);
|
|
1356
1365
|
}
|
|
1357
1366
|
return r < 0 ? null : {
|
|
1358
1367
|
point: this.splatWorldPosition(r, new d.Vector3()),
|
|
@@ -1374,20 +1383,20 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1374
1383
|
* every candidate is judged in world space, where the −Y probe direction is
|
|
1375
1384
|
* defined, so the vertical test never assumes local axis alignment.
|
|
1376
1385
|
*/
|
|
1377
|
-
queryHeight(
|
|
1378
|
-
if (this.activeCount === 0 || !(
|
|
1379
|
-
this.updateWorldMatrix(!0, !1), T.copy(
|
|
1380
|
-
const s = this.ensureQueryGrid(), r = (
|
|
1381
|
-
let a = -1 / 0,
|
|
1386
|
+
queryHeight(t, e, i = e / 2) {
|
|
1387
|
+
if (this.activeCount === 0 || !(e >= 0) || !(i >= 0)) return null;
|
|
1388
|
+
this.updateWorldMatrix(!0, !1), T.copy(t), this.worldToLocal(T);
|
|
1389
|
+
const s = this.ensureQueryGrid(), r = (e + i) / this.queryWorldMinScale(), n = i * i;
|
|
1390
|
+
let a = -1 / 0, h = -1;
|
|
1382
1391
|
if (s.forEachWithin(T.x, T.y, T.z, r, (l) => {
|
|
1383
1392
|
this.splatWorldPosition(l, k);
|
|
1384
|
-
const f =
|
|
1385
|
-
if (f < 0 || f >
|
|
1386
|
-
const x = k.x -
|
|
1387
|
-
x * x + p * p > n || k.y > a && (a = k.y,
|
|
1388
|
-
}),
|
|
1389
|
-
const
|
|
1390
|
-
return { point:
|
|
1393
|
+
const f = t.y - k.y;
|
|
1394
|
+
if (f < 0 || f > e) return;
|
|
1395
|
+
const x = k.x - t.x, p = k.z - t.z;
|
|
1396
|
+
x * x + p * p > n || k.y > a && (a = k.y, h = l);
|
|
1397
|
+
}), h < 0) return null;
|
|
1398
|
+
const c = this.splatWorldPosition(h, new d.Vector3());
|
|
1399
|
+
return { point: c, drop: t.y - c.y };
|
|
1391
1400
|
}
|
|
1392
1401
|
/**
|
|
1393
1402
|
* Renders this mesh from a second camera - into `target`, or the canvas when
|
|
@@ -1411,39 +1420,39 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1411
1420
|
*
|
|
1412
1421
|
* @param target - Destination render target, or `null`/omitted for the canvas.
|
|
1413
1422
|
*/
|
|
1414
|
-
renderView(
|
|
1423
|
+
renderView(t, e, i = null) {
|
|
1415
1424
|
if (this.disposed) return;
|
|
1416
|
-
this.lastRenderer =
|
|
1417
|
-
const s = this.sortForView(
|
|
1425
|
+
this.lastRenderer = e, t.updateMatrixWorld(), this.updateWorldMatrix(!0, !1), this.flushPendingUploads(e), i ? P.set(i.width, i.height) : e.getDrawingBufferSize(P), this.writeViewUniforms(t, P.x, P.y);
|
|
1426
|
+
const s = this.sortForView(t, e), r = e.getRenderTarget();
|
|
1418
1427
|
try {
|
|
1419
|
-
|
|
1428
|
+
e.setRenderTarget(i), e.render(this, t);
|
|
1420
1429
|
} finally {
|
|
1421
|
-
|
|
1430
|
+
e.setRenderTarget(r), s && (this.orderIsForeign = !0);
|
|
1422
1431
|
}
|
|
1423
1432
|
}
|
|
1424
1433
|
/**
|
|
1425
1434
|
* Sorts the shared order buffer for a secondary view's camera, bypassing the
|
|
1426
|
-
* primary view's sort scheduler and its
|
|
1435
|
+
* primary view's sort scheduler and its last-sorted pose record (that
|
|
1427
1436
|
* state belongs to `update()`'s camera). WebGPU dispatches synchronously into
|
|
1428
1437
|
* the render queue, so the following draw reads this order.
|
|
1429
1438
|
*/
|
|
1430
|
-
sortForView(
|
|
1431
|
-
return this.activeCount === 0 || !(
|
|
1439
|
+
sortForView(t, e) {
|
|
1440
|
+
return this.activeCount === 0 || !(e.backend.isWebGPUBackend === !0) || (this.currentModelView.multiplyMatrices(t.matrixWorldInverse, this.matrixWorld), this.refreshSortBounds(), this.sorter ?? (this.sorter = this.createSorter(e)), !this.sorter) ? !1 : (this.sorter.sort(this.currentModelView, this.activeCount, this.boundingSphereLocal), !0);
|
|
1432
1441
|
}
|
|
1433
1442
|
/** Rebuilds the query grid if the resident set changed since it was built. */
|
|
1434
1443
|
ensureQueryGrid() {
|
|
1435
1444
|
if (this.queryGrid && this.queryGridEpoch === this.queryEpoch) return this.queryGrid;
|
|
1436
|
-
const
|
|
1437
|
-
return this.queryGrid = new
|
|
1445
|
+
const t = this.sourceIndexAttribute.array.subarray(0, this.activeCount);
|
|
1446
|
+
return this.queryGrid = new Yt(this.backing.centers, t, this.activeCount), this.queryGridEpoch = this.queryEpoch, this.queryGrid;
|
|
1438
1447
|
}
|
|
1439
1448
|
/** World-space position of pool splat `poolIndex`, written into `out`. */
|
|
1440
|
-
splatWorldPosition(
|
|
1441
|
-
const i =
|
|
1442
|
-
return
|
|
1449
|
+
splatWorldPosition(t, e) {
|
|
1450
|
+
const i = t * 4;
|
|
1451
|
+
return e.set(
|
|
1443
1452
|
this.backing.centers[i],
|
|
1444
1453
|
this.backing.centers[i + 1],
|
|
1445
1454
|
this.backing.centers[i + 2]
|
|
1446
|
-
),
|
|
1455
|
+
), e.applyMatrix4(this.matrixWorld);
|
|
1447
1456
|
}
|
|
1448
1457
|
/**
|
|
1449
1458
|
* The smallest world axis scale, for conservatively mapping a world radius
|
|
@@ -1451,7 +1460,7 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1451
1460
|
* of `r / minAxisScale`, for any per-axis (non-uniform) scale.
|
|
1452
1461
|
*/
|
|
1453
1462
|
queryWorldMinScale() {
|
|
1454
|
-
return this.getWorldScale(
|
|
1463
|
+
return this.getWorldScale(z), Math.min(Math.abs(z.x), Math.abs(z.y), Math.abs(z.z)) || 1;
|
|
1455
1464
|
}
|
|
1456
1465
|
/**
|
|
1457
1466
|
* Releases every GPU resource and worker this mesh owns. Idempotent: a
|
|
@@ -1460,24 +1469,24 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1460
1469
|
* become no-ops after dispose.
|
|
1461
1470
|
*/
|
|
1462
1471
|
dispose() {
|
|
1463
|
-
var
|
|
1472
|
+
var t;
|
|
1464
1473
|
if (!this.disposed) {
|
|
1465
|
-
this.disposed = !0, (
|
|
1466
|
-
for (const
|
|
1467
|
-
this.pool.releaseRows(
|
|
1474
|
+
this.disposed = !0, (t = this.sorter) == null || t.dispose(), this.sorter = null, this.geometry.dispose(), this.material.dispose(), this.picker.dispose(), this.relightPlaceholder.dispose();
|
|
1475
|
+
for (const e of this.ranges.values())
|
|
1476
|
+
this.pool.releaseRows(e.startRow, e.rowCount);
|
|
1468
1477
|
this.pool.unregister(this), this.ownsPool && this.pool.dispose();
|
|
1469
|
-
for (const
|
|
1470
|
-
!this.ownsPool && this.pool.isPoolTexture(
|
|
1471
|
-
for (const
|
|
1478
|
+
for (const e of this.dataTextures)
|
|
1479
|
+
!this.ownsPool && this.pool.isPoolTexture(e) || e.dispose();
|
|
1480
|
+
for (const e of this.channels.values()) e.texture.dispose();
|
|
1472
1481
|
this.channels.clear();
|
|
1473
|
-
for (const
|
|
1474
|
-
for (const i of
|
|
1482
|
+
for (const e of this.uploadStaging.values())
|
|
1483
|
+
for (const i of e.values()) i.dispose();
|
|
1475
1484
|
this.uploadStaging.clear(), this.halfEncodeBuffers.clear(), this.lastRenderer && (Ut(this.lastRenderer, [this.sourceIndexAttribute]), this.lastRenderer = null), this.pendingUploadRows = [], this.workerDirtyRows = [], this.queryGrid = null, this.ranges.clear();
|
|
1476
1485
|
}
|
|
1477
1486
|
}
|
|
1478
1487
|
/** Matches the viewport / focal / local-camera uniforms written by {@link update}. */
|
|
1479
|
-
refreshProjectionUniforms(
|
|
1480
|
-
|
|
1488
|
+
refreshProjectionUniforms(t, e) {
|
|
1489
|
+
e.getDrawingBufferSize(P), this.writeViewUniforms(t, P.x, P.y);
|
|
1481
1490
|
}
|
|
1482
1491
|
/**
|
|
1483
1492
|
* Writes the three view-dependent uniforms - viewport, focal (splat screen
|
|
@@ -1492,10 +1501,10 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1492
1501
|
* head, so both eyes shade from one cyclopean point while each still
|
|
1493
1502
|
* projects through its own eye.
|
|
1494
1503
|
*/
|
|
1495
|
-
writeViewUniforms(
|
|
1496
|
-
this.viewport.value.set(
|
|
1497
|
-
const r =
|
|
1498
|
-
this.focal.value.set(r[0] *
|
|
1504
|
+
writeViewUniforms(t, e, i, s = t) {
|
|
1505
|
+
this.viewport.value.set(e, i);
|
|
1506
|
+
const r = t.projectionMatrix.elements, n = r[5] * i / 2;
|
|
1507
|
+
this.focal.value.set(r[0] * e / 2, n), s.getWorldPosition(this.localCameraPosition.value), this.worldToLocal(this.localCameraPosition.value), this.foveationMode === "frontier" && n > 0 && (this.pixelScaleLimit.value = this.foveationLimitPx / n);
|
|
1499
1508
|
}
|
|
1500
1509
|
/**
|
|
1501
1510
|
* Self-adjusts {@link foveationLimitPx} so the frontier cut's drawn-splat count
|
|
@@ -1511,84 +1520,84 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1511
1520
|
* here against a per-eye viewport there) makes the estimator judge a cut the
|
|
1512
1521
|
* shader is not applying, and the ratchet runs away toward the maximum.
|
|
1513
1522
|
*/
|
|
1514
|
-
adaptFoveationLimit(
|
|
1523
|
+
adaptFoveationLimit(t, e) {
|
|
1515
1524
|
if (this.foveationMode !== "frontier") return;
|
|
1516
1525
|
const i = performance.now();
|
|
1517
|
-
if (i - this.lastFoveationAdaptAt <
|
|
1526
|
+
if (i - this.lastFoveationAdaptAt < Qt) return;
|
|
1518
1527
|
this.lastFoveationAdaptAt = i;
|
|
1519
|
-
const s =
|
|
1528
|
+
const s = t.projectionMatrix.elements[5] * e / 2;
|
|
1520
1529
|
if (!(s > 0)) return;
|
|
1521
1530
|
const r = this.foveationLimitPx / s;
|
|
1522
|
-
|
|
1523
|
-
const n = at.elements, { covarianceA: a, covarianceB:
|
|
1531
|
+
t.getWorldPosition(_), this.worldToLocal(_), ot.copy(t.matrixWorld).invert(), at.multiplyMatrices(ot, this.matrixWorld).premultiply(t.projectionMatrix);
|
|
1532
|
+
const n = at.elements, { covarianceA: a, covarianceB: h, centers: c } = this.backing, l = h.length / 4;
|
|
1524
1533
|
let f = 0, x = 0;
|
|
1525
|
-
for (let m = 0; m < l; m +=
|
|
1526
|
-
const S = m * 4, A =
|
|
1534
|
+
for (let m = 0; m < l; m += ct) {
|
|
1535
|
+
const S = m * 4, A = h[S + 3];
|
|
1527
1536
|
if (A === 0) continue;
|
|
1528
1537
|
f++;
|
|
1529
|
-
const R =
|
|
1538
|
+
const R = c[S], y = c[S + 1], M = c[S + 2], I = (n[3] * R + n[7] * y + n[11] * M + n[15]) * 1.2;
|
|
1530
1539
|
if (n[2] * R + n[6] * y + n[10] * M + n[14] <= -I) continue;
|
|
1531
1540
|
const B = n[0] * R + n[4] * y + n[8] * M + n[12];
|
|
1532
1541
|
if (B > I || B < -I) continue;
|
|
1533
1542
|
const E = n[1] * R + n[5] * y + n[9] * M + n[13];
|
|
1534
1543
|
if (E > I || E < -I) continue;
|
|
1535
|
-
const W = a[S] + a[S + 3] +
|
|
1544
|
+
const W = a[S] + a[S + 3] + h[S + 1], L = 2 * Math.sqrt(Math.max(W, 0) / 3), C = A < 0, ut = Math.abs(A), N = R - _.x, X = y - _.y, j = M - _.z, $ = r * Math.sqrt(N * N + X * X + j * j), dt = C ? 0 : L;
|
|
1536
1545
|
ut > $ && dt <= $ && x++;
|
|
1537
1546
|
}
|
|
1538
1547
|
if (f === 0) return;
|
|
1539
|
-
const p = x *
|
|
1540
|
-
p > w * 1.1 ? this.foveationLimitPx = Math.min(this.foveationLimitPx * 1.15,
|
|
1548
|
+
const p = x * ct, w = this.foveationDrawBudget;
|
|
1549
|
+
p > w * 1.1 ? this.foveationLimitPx = Math.min(this.foveationLimitPx * 1.15, Jt) : p < w * 0.7 && (this.foveationLimitPx = Math.max(this.foveationLimitPx / 1.12, this.foveationTargetPx));
|
|
1541
1550
|
}
|
|
1542
1551
|
/** Records a written row span for GPU upload and the sort-worker mirror. */
|
|
1543
|
-
markRowsWritten(
|
|
1552
|
+
markRowsWritten(t, e) {
|
|
1544
1553
|
var i;
|
|
1545
|
-
this.pendingUploadRows.push({ start:
|
|
1554
|
+
this.pendingUploadRows.push({ start: t, count: e }), ((i = this.sorter) == null ? void 0 : i.kind) === "worker" && this.workerDirtyRows.push({ start: t, count: e });
|
|
1546
1555
|
}
|
|
1547
1556
|
/** Appends one newly active pool range to the packed source-index list. */
|
|
1548
|
-
activateRecord(
|
|
1549
|
-
const
|
|
1550
|
-
if (
|
|
1557
|
+
activateRecord(t) {
|
|
1558
|
+
const e = t.activePrefix ?? t.count;
|
|
1559
|
+
if (e === 0) return;
|
|
1551
1560
|
this.queryEpoch++;
|
|
1552
1561
|
const i = this.sourceIndexAttribute.array, s = this.getPoolIndexTemplate(), r = this.activeCount;
|
|
1553
|
-
i.set(s.subarray(
|
|
1554
|
-
s.subarray(r, r +
|
|
1555
|
-
|
|
1556
|
-
), this.activeCount +=
|
|
1562
|
+
i.set(s.subarray(t.start, t.start + e), r), this.activeSlotByPoolIndex.set(
|
|
1563
|
+
s.subarray(r, r + e),
|
|
1564
|
+
t.start
|
|
1565
|
+
), this.activeCount += e, this.commitActiveListMutation(r, e);
|
|
1557
1566
|
}
|
|
1558
1567
|
/**
|
|
1559
1568
|
* Removes one active range and backfills its holes from the packed tail.
|
|
1560
1569
|
* The common contiguous case uses one native block copy; the fallback
|
|
1561
1570
|
* handles a range split by earlier tail backfills in O(record.count).
|
|
1562
1571
|
*/
|
|
1563
|
-
deactivateRecord(
|
|
1564
|
-
const
|
|
1565
|
-
if (
|
|
1572
|
+
deactivateRecord(t) {
|
|
1573
|
+
const e = t.activePrefix ?? t.count;
|
|
1574
|
+
if (e === 0) return;
|
|
1566
1575
|
this.queryEpoch++;
|
|
1567
|
-
const i = this.sourceIndexAttribute.array, s = this.activeSlotByPoolIndex[
|
|
1576
|
+
const i = this.sourceIndexAttribute.array, s = this.activeSlotByPoolIndex[t.start];
|
|
1568
1577
|
let r = !0;
|
|
1569
|
-
for (let
|
|
1570
|
-
if (this.activeSlotByPoolIndex[
|
|
1578
|
+
for (let h = 1; h < e; h++)
|
|
1579
|
+
if (this.activeSlotByPoolIndex[t.start + h] !== s + h) {
|
|
1571
1580
|
r = !1;
|
|
1572
1581
|
break;
|
|
1573
1582
|
}
|
|
1574
1583
|
let n = this.activeCount, a = 0;
|
|
1575
1584
|
if (r) {
|
|
1576
|
-
const
|
|
1577
|
-
if (
|
|
1578
|
-
i.copyWithin(s, this.activeCount -
|
|
1579
|
-
for (let l = 0; l <
|
|
1585
|
+
const h = this.activeCount - e, c = Math.min(e, Math.max(0, h - s));
|
|
1586
|
+
if (c > 0) {
|
|
1587
|
+
i.copyWithin(s, this.activeCount - c, this.activeCount);
|
|
1588
|
+
for (let l = 0; l < c; l++) {
|
|
1580
1589
|
const f = i[s + l];
|
|
1581
1590
|
this.activeSlotByPoolIndex[f] = s + l;
|
|
1582
1591
|
}
|
|
1583
|
-
n = s, a = s +
|
|
1592
|
+
n = s, a = s + c;
|
|
1584
1593
|
}
|
|
1585
|
-
this.activeCount =
|
|
1594
|
+
this.activeCount = h;
|
|
1586
1595
|
} else {
|
|
1587
|
-
for (let
|
|
1588
|
-
const
|
|
1589
|
-
if (
|
|
1596
|
+
for (let h = t.start; h < t.start + e; h++) {
|
|
1597
|
+
const c = this.activeSlotByPoolIndex[h], l = this.activeCount - 1;
|
|
1598
|
+
if (c !== l) {
|
|
1590
1599
|
const f = i[l];
|
|
1591
|
-
i[
|
|
1600
|
+
i[c] = f, this.activeSlotByPoolIndex[f] = c, n = Math.min(n, c), a = Math.max(a, c + 1);
|
|
1592
1601
|
}
|
|
1593
1602
|
this.activeCount--;
|
|
1594
1603
|
}
|
|
@@ -1597,25 +1606,25 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1597
1606
|
this.commitActiveListMutation(n, Math.max(0, a - n));
|
|
1598
1607
|
}
|
|
1599
1608
|
/** Uploads only the changed packed slots and invalidates the current depth order. */
|
|
1600
|
-
commitActiveListMutation(
|
|
1609
|
+
commitActiveListMutation(t, e) {
|
|
1601
1610
|
var i;
|
|
1602
|
-
if (
|
|
1611
|
+
if (e > 0 && (q(this.sourceIndexAttribute, t, e), this.sourceIndexAttribute.needsUpdate = !0), ((i = this.sorter) == null ? void 0 : i.kind) === "worker" || this.sorter === null) {
|
|
1603
1612
|
const s = this.sourceIndexAttribute.array, r = this.splatIndexAttribute.array;
|
|
1604
|
-
this.drawListSorted ? (r.set(s.subarray(0, this.activeCount)), this.activeCount > 0 &&
|
|
1613
|
+
this.drawListSorted ? (r.set(s.subarray(0, this.activeCount)), this.activeCount > 0 && q(this.splatIndexAttribute, 0, this.activeCount), this.splatIndexAttribute.needsUpdate = !0, this.drawListSorted = !1) : e > 0 && (r.set(s.subarray(t, t + e), t), q(this.splatIndexAttribute, t, e), this.splatIndexAttribute.needsUpdate = !0);
|
|
1605
1614
|
}
|
|
1606
1615
|
this.geometry.instanceCount = this.activeCount, this.activeListVersion++, this.sortScheduler.invalidateContent();
|
|
1607
1616
|
}
|
|
1608
1617
|
/** Rewrites the active-splat list (pool indices, range by range). */
|
|
1609
1618
|
rebuildActiveList() {
|
|
1610
1619
|
var s;
|
|
1611
|
-
const
|
|
1620
|
+
const t = this.sourceIndexAttribute.array, e = this.getPoolIndexTemplate();
|
|
1612
1621
|
let i = 0;
|
|
1613
1622
|
for (const r of this.ranges.values()) {
|
|
1614
1623
|
if (!r.active) continue;
|
|
1615
1624
|
const n = r.activePrefix ?? r.count;
|
|
1616
|
-
|
|
1625
|
+
t.set(e.subarray(r.start, r.start + n), i), this.activeSlotByPoolIndex.set(e.subarray(i, i + n), r.start), i += n;
|
|
1617
1626
|
}
|
|
1618
|
-
this.activeCount = i, this.sourceIndexAttribute.clearUpdateRanges(), i > 0 && this.sourceIndexAttribute.addUpdateRange(0, i), this.sourceIndexAttribute.needsUpdate = !0, (((s = this.sorter) == null ? void 0 : s.kind) === "worker" || this.sorter === null) && (this.splatIndexAttribute.array.set(
|
|
1627
|
+
this.activeCount = i, this.sourceIndexAttribute.clearUpdateRanges(), i > 0 && this.sourceIndexAttribute.addUpdateRange(0, i), this.sourceIndexAttribute.needsUpdate = !0, (((s = this.sorter) == null ? void 0 : s.kind) === "worker" || this.sorter === null) && (this.splatIndexAttribute.array.set(t.subarray(0, i)), this.splatIndexAttribute.clearUpdateRanges(), i > 0 && this.splatIndexAttribute.addUpdateRange(0, i), this.splatIndexAttribute.needsUpdate = !0, this.drawListSorted = !1), this.geometry.instanceCount = i, this.activeListVersion++, this.sortScheduler.invalidateContent();
|
|
1619
1628
|
}
|
|
1620
1629
|
/** The pool's reusable index ramp; shared by every mesh drawing from it. */
|
|
1621
1630
|
getPoolIndexTemplate() {
|
|
@@ -1626,15 +1635,15 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1626
1635
|
* through a staging texture. (Constructor-time writes never appear here;
|
|
1627
1636
|
* they ride the textures' initial full upload instead.)
|
|
1628
1637
|
*/
|
|
1629
|
-
flushPendingUploads(
|
|
1638
|
+
flushPendingUploads(t) {
|
|
1630
1639
|
if (this.pendingUploadRows.length > 0) {
|
|
1631
|
-
const
|
|
1632
|
-
this.uploadRows(
|
|
1640
|
+
const e = this.poolFloatTextures === "float16" ? d.HalfFloatType : d.FloatType;
|
|
1641
|
+
this.uploadRows(t, this.pendingUploadRows, d.RGBAFormat, 4, [
|
|
1633
1642
|
{
|
|
1634
1643
|
key: "centers",
|
|
1635
1644
|
texture: this.dataTextures[0],
|
|
1636
1645
|
data: this.backing.centers,
|
|
1637
|
-
type:
|
|
1646
|
+
type: e,
|
|
1638
1647
|
encodeHalf: this.poolFloatTextures === "float16"
|
|
1639
1648
|
},
|
|
1640
1649
|
{
|
|
@@ -1647,7 +1656,7 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1647
1656
|
key: "covarianceA",
|
|
1648
1657
|
texture: this.dataTextures[2],
|
|
1649
1658
|
data: this.backing.covarianceA,
|
|
1650
|
-
type:
|
|
1659
|
+
type: e,
|
|
1651
1660
|
encodeHalf: this.poolFloatTextures === "float16"
|
|
1652
1661
|
},
|
|
1653
1662
|
{
|
|
@@ -1657,7 +1666,7 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1657
1666
|
type: d.FloatType
|
|
1658
1667
|
}
|
|
1659
1668
|
]), this.shPackedTextures.length > 0 && this.uploadRows(
|
|
1660
|
-
|
|
1669
|
+
t,
|
|
1661
1670
|
this.pendingUploadRows,
|
|
1662
1671
|
d.RGBAIntegerFormat,
|
|
1663
1672
|
4,
|
|
@@ -1669,10 +1678,10 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1669
1678
|
}))
|
|
1670
1679
|
), this.pendingUploadRows = [];
|
|
1671
1680
|
}
|
|
1672
|
-
for (const [
|
|
1673
|
-
i.pendingRows.length !== 0 && (this.uploadRows(
|
|
1681
|
+
for (const [e, i] of this.channels.entries())
|
|
1682
|
+
i.pendingRows.length !== 0 && (this.uploadRows(t, i.pendingRows, d.RedFormat, 1, [
|
|
1674
1683
|
{
|
|
1675
|
-
key: `channel:${
|
|
1684
|
+
key: `channel:${e}`,
|
|
1676
1685
|
texture: i.texture,
|
|
1677
1686
|
data: i.backing,
|
|
1678
1687
|
type: i.textureType
|
|
@@ -1685,8 +1694,8 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1685
1694
|
* upload carries half bits (staging is skipped for that path).
|
|
1686
1695
|
*/
|
|
1687
1696
|
syncHalfFloatPoolImages() {
|
|
1688
|
-
const
|
|
1689
|
-
G(this.backing.centers, i), G(this.backing.covarianceA, s),
|
|
1697
|
+
const t = this.dataTextures[0], e = this.dataTextures[2], i = t.image.data, s = e.image.data;
|
|
1698
|
+
G(this.backing.centers, i), G(this.backing.covarianceA, s), t.needsUpdate = !0, e.needsUpdate = !0;
|
|
1690
1699
|
}
|
|
1691
1700
|
/**
|
|
1692
1701
|
* Uploads the given row spans of one or more same-layout pool textures via
|
|
@@ -1694,17 +1703,17 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1694
1703
|
* upload as one rectangle each - the copyTextureToTexture call count, not
|
|
1695
1704
|
* the pixel volume, dominates.
|
|
1696
1705
|
*/
|
|
1697
|
-
uploadRows(
|
|
1698
|
-
const n = v.DATA_TEXTURE_WIDTH, a =
|
|
1699
|
-
for (const
|
|
1700
|
-
const l =
|
|
1701
|
-
l &&
|
|
1706
|
+
uploadRows(t, e, i, s, r) {
|
|
1707
|
+
const n = v.DATA_TEXTURE_WIDTH, a = e.sort((c, l) => c.start - l.start), h = [];
|
|
1708
|
+
for (const c of a) {
|
|
1709
|
+
const l = h[h.length - 1];
|
|
1710
|
+
l && c.start <= l.start + l.count ? l.count = Math.max(l.count, c.start + c.count - l.start) : h.push({ start: c.start, count: c.count });
|
|
1702
1711
|
}
|
|
1703
|
-
for (const
|
|
1712
|
+
for (const c of h)
|
|
1704
1713
|
for (const { key: l, texture: f, data: x, type: p, encodeHalf: w } of r) {
|
|
1705
1714
|
const m = x.subarray(
|
|
1706
|
-
|
|
1707
|
-
(
|
|
1715
|
+
c.start * n * s,
|
|
1716
|
+
(c.start + c.count) * n * s
|
|
1708
1717
|
);
|
|
1709
1718
|
let S = m;
|
|
1710
1719
|
if (w) {
|
|
@@ -1715,19 +1724,19 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1715
1724
|
l,
|
|
1716
1725
|
S,
|
|
1717
1726
|
n,
|
|
1718
|
-
|
|
1727
|
+
c.count,
|
|
1719
1728
|
i,
|
|
1720
1729
|
p
|
|
1721
1730
|
);
|
|
1722
|
-
|
|
1731
|
+
t.copyTextureToTexture(A, f, null, Kt.set(0, c.start));
|
|
1723
1732
|
}
|
|
1724
1733
|
}
|
|
1725
1734
|
/** Reuses a same-length half encode buffer for one staging key. */
|
|
1726
|
-
acquireHalfEncodeBuffer(
|
|
1727
|
-
const i = this.halfEncodeBuffers.get(
|
|
1728
|
-
if (i && i.length ===
|
|
1729
|
-
const s = new Uint16Array(
|
|
1730
|
-
return this.halfEncodeBuffers.set(
|
|
1735
|
+
acquireHalfEncodeBuffer(t, e) {
|
|
1736
|
+
const i = this.halfEncodeBuffers.get(t);
|
|
1737
|
+
if (i && i.length === e) return i;
|
|
1738
|
+
const s = new Uint16Array(e);
|
|
1739
|
+
return this.halfEncodeBuffers.set(t, s), s;
|
|
1731
1740
|
}
|
|
1732
1741
|
/**
|
|
1733
1742
|
* Reuses one of the recent same-sized staging textures for this upload. WebGPU
|
|
@@ -1735,25 +1744,25 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1735
1744
|
* a small LRU per channel avoids recreating all core/SH staging resources when
|
|
1736
1745
|
* a stream alternates among a few chunk heights, while bounding GPU memory.
|
|
1737
1746
|
*/
|
|
1738
|
-
acquireUploadStaging(
|
|
1747
|
+
acquireUploadStaging(t, e, i, s, r, n) {
|
|
1739
1748
|
var l;
|
|
1740
|
-
let a = this.uploadStaging.get(
|
|
1741
|
-
a || (a = /* @__PURE__ */ new Map(), this.uploadStaging.set(
|
|
1742
|
-
const
|
|
1743
|
-
if (
|
|
1744
|
-
return
|
|
1745
|
-
const
|
|
1746
|
-
if (r === d.RGBAIntegerFormat && (
|
|
1749
|
+
let a = this.uploadStaging.get(t);
|
|
1750
|
+
a || (a = /* @__PURE__ */ new Map(), this.uploadStaging.set(t, a));
|
|
1751
|
+
const h = a.get(s);
|
|
1752
|
+
if (h)
|
|
1753
|
+
return h.image = { data: e, width: i, height: s }, h.needsUpdate = !0, a.delete(s), a.set(s, h), h;
|
|
1754
|
+
const c = new d.DataTexture(e, i, s, r, n);
|
|
1755
|
+
if (r === d.RGBAIntegerFormat && (c.magFilter = d.NearestFilter, c.minFilter = d.NearestFilter), c.needsUpdate = !0, a.set(s, c), a.size > Zt) {
|
|
1747
1756
|
const f = a.keys().next().value;
|
|
1748
1757
|
(l = a.get(f)) == null || l.dispose(), a.delete(f);
|
|
1749
1758
|
}
|
|
1750
|
-
return this.updateTimings.stagingTextureAllocations++,
|
|
1759
|
+
return this.updateTimings.stagingTextureAllocations++, c;
|
|
1751
1760
|
}
|
|
1752
1761
|
/** Gathers everything the material graph reads. See `splat-mesh-material.ts`. */
|
|
1753
|
-
graphInputs(
|
|
1762
|
+
graphInputs(t, e) {
|
|
1754
1763
|
return {
|
|
1755
|
-
textures:
|
|
1756
|
-
sh:
|
|
1764
|
+
textures: t,
|
|
1765
|
+
sh: e,
|
|
1757
1766
|
sourcePlacement: this.perSourceSort,
|
|
1758
1767
|
// The uniform node instances, shared with the pick graph on purpose: one
|
|
1759
1768
|
// per-frame write then reaches both.
|
|
@@ -1791,22 +1800,22 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1791
1800
|
modifiers: this.modifierList
|
|
1792
1801
|
};
|
|
1793
1802
|
}
|
|
1794
|
-
buildMaterial(
|
|
1803
|
+
buildMaterial(t, e) {
|
|
1795
1804
|
Z(
|
|
1796
1805
|
this.material,
|
|
1797
1806
|
"display",
|
|
1798
|
-
this.graphInputs(
|
|
1807
|
+
this.graphInputs(t, e)
|
|
1799
1808
|
), this.picker.rebuildMaterial();
|
|
1800
1809
|
}
|
|
1801
|
-
requestSortIfNeeded(
|
|
1810
|
+
requestSortIfNeeded(t, e) {
|
|
1802
1811
|
if (this.deferSortRequestOnce && (this.deferSortRequestOnce = !1, this.activeListVersion === this.sortedActiveListVersion && !this.orderIsForeign) || this.activeCount === 0) return;
|
|
1803
|
-
this.currentModelView.multiplyMatrices(
|
|
1804
|
-
const i =
|
|
1812
|
+
this.currentModelView.multiplyMatrices(t.matrixWorldInverse, this.matrixWorld), this.writeSortState(t);
|
|
1813
|
+
const i = e.backend.isWebGPUBackend === !0, s = i ? performance.now() : 0;
|
|
1805
1814
|
if (!this.orderIsForeign) {
|
|
1806
1815
|
if (i) {
|
|
1807
1816
|
if (!this.sortScheduler.shouldSubmit(
|
|
1808
|
-
this.
|
|
1809
|
-
this.
|
|
1817
|
+
this.currentSortState,
|
|
1818
|
+
this.lastSortedState,
|
|
1810
1819
|
this.activeCount,
|
|
1811
1820
|
s
|
|
1812
1821
|
))
|
|
@@ -1816,11 +1825,11 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1816
1825
|
// still invalidated the draw order (the swap reset it to unsorted
|
|
1817
1826
|
// active order) - skipping here would leave the scene blend-order
|
|
1818
1827
|
// broken until the camera next moved.
|
|
1819
|
-
!this.sortScheduler.hasPendingForce() && this.
|
|
1828
|
+
!this.sortScheduler.hasPendingForce() && this.currentSortState.equals(this.lastSortedState)
|
|
1820
1829
|
)
|
|
1821
1830
|
return;
|
|
1822
1831
|
}
|
|
1823
|
-
this.refreshSortBounds(), this.sorter ?? (this.sorter = this.createSorter(
|
|
1832
|
+
this.refreshSortBounds(), this.sorter ?? (this.sorter = this.createSorter(e)), this.sorter && this.sorter.sort(this.currentModelView, this.activeCount, this.boundingSphereLocal) && (this.lastSortedState.copy(this.currentSortState), this.sortedActiveListVersion = this.activeListVersion, this.orderIsForeign = !1, this.sortScheduler.markAccepted(s));
|
|
1824
1833
|
}
|
|
1825
1834
|
/**
|
|
1826
1835
|
* Refreshes {@link boundingSphereLocal} - the sphere the sorter quantizes
|
|
@@ -1832,16 +1841,16 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1832
1841
|
this.boundsDirty && (this.localBounds.getBoundingSphere(this.boundingSphereLocal), this.boundsDirty = !1);
|
|
1833
1842
|
}
|
|
1834
1843
|
/** The data texture backing a channel defined with {@link defineChannel}. */
|
|
1835
|
-
channelTexture(
|
|
1836
|
-
var
|
|
1837
|
-
return (
|
|
1844
|
+
channelTexture(t) {
|
|
1845
|
+
var e;
|
|
1846
|
+
return (e = this.channels.get(t)) == null ? void 0 : e.texture;
|
|
1838
1847
|
}
|
|
1839
1848
|
/** CPU backing storage of a float channel, for internal CPU-side consumers. */
|
|
1840
|
-
channelBacking(
|
|
1841
|
-
const
|
|
1842
|
-
if (!
|
|
1843
|
-
throw new Error(`SplatMesh: float channel "${
|
|
1844
|
-
return
|
|
1849
|
+
channelBacking(t) {
|
|
1850
|
+
const e = this.channels.get(t);
|
|
1851
|
+
if (!e || !(e.backing instanceof Float32Array))
|
|
1852
|
+
throw new Error(`SplatMesh: float channel "${t}" is missing.`);
|
|
1853
|
+
return e.backing;
|
|
1845
1854
|
}
|
|
1846
1855
|
/**
|
|
1847
1856
|
* Forces the next {@link update} to re-sort even if the camera has not moved
|
|
@@ -1851,49 +1860,72 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1851
1860
|
invalidateSort() {
|
|
1852
1861
|
this.sortScheduler.invalidate(), this.boundsDirty = !0;
|
|
1853
1862
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1863
|
+
/**
|
|
1864
|
+
* Writes the camera/mesh state that can change the selected ordering key.
|
|
1865
|
+
* Radial distance is invariant under camera rotation, so its state contains
|
|
1866
|
+
* the mesh's world linear transform and camera-relative translation only.
|
|
1867
|
+
*/
|
|
1868
|
+
writeSortState(t) {
|
|
1869
|
+
if (this.sortMetric === "depth") {
|
|
1870
|
+
this.currentSortState.copy(this.currentModelView);
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
Dt(this.matrixWorld, t.matrixWorld, this.currentSortState);
|
|
1874
|
+
}
|
|
1875
|
+
createSorter(t) {
|
|
1876
|
+
if (!(t.backend.isWebGPUBackend === !0) || this.sortStrategy === "worker")
|
|
1877
|
+
return new qt(
|
|
1878
|
+
{
|
|
1879
|
+
capacity: this.capacity,
|
|
1880
|
+
rowWidth: v.DATA_TEXTURE_WIDTH,
|
|
1881
|
+
centers: this.backing.centers,
|
|
1882
|
+
perSource: this.perSourceSort ? {
|
|
1883
|
+
sourceIds: this.perSourceSort.sourceIds,
|
|
1884
|
+
matrices: this.perSourceSort.matrices
|
|
1885
|
+
} : void 0,
|
|
1886
|
+
splatIndexAttribute: this.splatIndexAttribute,
|
|
1887
|
+
takeDirtyRows: () => {
|
|
1888
|
+
const s = this.workerDirtyRows;
|
|
1889
|
+
return this.workerDirtyRows = [], s;
|
|
1890
|
+
},
|
|
1891
|
+
getActiveSpans: () => {
|
|
1892
|
+
const s = new Uint32Array(this.ranges.size * 2);
|
|
1893
|
+
let r = 0;
|
|
1894
|
+
for (const n of this.ranges.values()) {
|
|
1895
|
+
if (!n.active) continue;
|
|
1896
|
+
const a = n.activePrefix ?? n.count;
|
|
1897
|
+
a !== 0 && (s[r++] = n.start, s[r++] = a);
|
|
1898
|
+
}
|
|
1899
|
+
return s.subarray(0, r);
|
|
1900
|
+
},
|
|
1901
|
+
onOrderApplied: () => {
|
|
1902
|
+
this.drawListSorted = !0;
|
|
1876
1903
|
}
|
|
1877
|
-
return s.subarray(0, r);
|
|
1878
1904
|
},
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
}
|
|
1882
|
-
});
|
|
1905
|
+
this.sortMetric
|
|
1906
|
+
);
|
|
1883
1907
|
const i = {
|
|
1884
|
-
renderer:
|
|
1908
|
+
renderer: t,
|
|
1885
1909
|
capacity: this.poolRows * v.DATA_TEXTURE_WIDTH,
|
|
1886
1910
|
centersTexture: this.centersTexture,
|
|
1887
1911
|
dataTextureWidth: v.DATA_TEXTURE_WIDTH,
|
|
1888
1912
|
splatIndexAttribute: this.splatIndexAttribute,
|
|
1889
1913
|
sourceIndexAttribute: this.sourceIndexAttribute
|
|
1890
1914
|
};
|
|
1891
|
-
return this.perSourceSort ? new tt({
|
|
1915
|
+
return this.perSourceSort ? new tt({
|
|
1916
|
+
...i,
|
|
1917
|
+
perSource: this.perSourceSort,
|
|
1918
|
+
sortMetric: this.sortMetric
|
|
1919
|
+
}) : this.sortStrategy === "radix" || this.sortStrategy === "exact" ? (this.ensureRadixSorter(), this.RadixSorterCtor ? new this.RadixSorterCtor({
|
|
1920
|
+
...i,
|
|
1921
|
+
exactDepth: this.sortStrategy === "exact",
|
|
1922
|
+
sortMetric: this.sortMetric
|
|
1923
|
+
}) : null) : new tt({ ...i, sortMetric: this.sortMetric });
|
|
1892
1924
|
}
|
|
1893
1925
|
/** Prefetches the experimental radix sorter; safe to call repeatedly. */
|
|
1894
1926
|
ensureRadixSorter() {
|
|
1895
|
-
this.RadixSorterCtor || this.radixSorterLoad || (this.radixSorterLoad = import("./radix-sorter-
|
|
1896
|
-
this.RadixSorterCtor =
|
|
1927
|
+
this.RadixSorterCtor || this.radixSorterLoad || (this.radixSorterLoad = import("./radix-sorter-tSffmerk.js").then((t) => {
|
|
1928
|
+
this.RadixSorterCtor = t.RadixSorter;
|
|
1897
1929
|
}).finally(() => {
|
|
1898
1930
|
this.radixSorterLoad = null;
|
|
1899
1931
|
}));
|
|
@@ -1901,13 +1933,13 @@ const Yt = 4, v = class v extends d.Mesh {
|
|
|
1901
1933
|
};
|
|
1902
1934
|
o(v, "DATA_TEXTURE_WIDTH", xt);
|
|
1903
1935
|
let nt = v;
|
|
1904
|
-
const F = new d.Box3(),
|
|
1905
|
-
function
|
|
1936
|
+
const F = new d.Box3(), Kt = new d.Vector2(), T = new d.Vector3(), k = new d.Vector3(), z = new d.Vector3(), P = new d.Vector2(), _ = new d.Vector3(), ot = new d.Matrix4(), at = new d.Matrix4(), Qt = 180, ct = 31, Jt = 64;
|
|
1937
|
+
function ht(g) {
|
|
1906
1938
|
if (g !== void 0 && (!Number.isFinite(g) || g <= 0))
|
|
1907
1939
|
throw new RangeError("SplatMesh maxStdDev must be a finite number greater than 0.");
|
|
1908
1940
|
return g;
|
|
1909
1941
|
}
|
|
1910
|
-
function
|
|
1942
|
+
function te(g) {
|
|
1911
1943
|
if (g !== void 0 && (!Number.isFinite(g) || g < 0))
|
|
1912
1944
|
throw new RangeError("SplatMesh minSplatSizePx must be a finite number >= 0.");
|
|
1913
1945
|
return g;
|
|
@@ -1918,25 +1950,25 @@ function H(g) {
|
|
|
1918
1950
|
throw new RangeError("SplatMesh screen-radius cull must be a finite number ≥ 0.");
|
|
1919
1951
|
return g;
|
|
1920
1952
|
}
|
|
1921
|
-
const
|
|
1922
|
-
function
|
|
1923
|
-
if (g === void 0) return
|
|
1953
|
+
const ee = 1;
|
|
1954
|
+
function ie(g) {
|
|
1955
|
+
if (g === void 0) return ee;
|
|
1924
1956
|
if (!Number.isFinite(g) || g <= 0)
|
|
1925
1957
|
throw new RangeError("SplatMesh foveationTargetPx must be a finite number > 0.");
|
|
1926
1958
|
return g;
|
|
1927
1959
|
}
|
|
1928
|
-
const
|
|
1929
|
-
function
|
|
1930
|
-
if (g === void 0) return
|
|
1960
|
+
const se = 9e5;
|
|
1961
|
+
function re(g) {
|
|
1962
|
+
if (g === void 0) return se;
|
|
1931
1963
|
if (!Number.isFinite(g) || g <= 0)
|
|
1932
1964
|
throw new RangeError("SplatMesh foveationDrawBudget must be a finite number > 0.");
|
|
1933
1965
|
return g;
|
|
1934
1966
|
}
|
|
1935
1967
|
export {
|
|
1936
|
-
|
|
1968
|
+
se as D,
|
|
1937
1969
|
nt as S,
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1970
|
+
ee as a,
|
|
1971
|
+
Vt as c,
|
|
1972
|
+
_t as y
|
|
1941
1973
|
};
|
|
1942
|
-
//# sourceMappingURL=splat-mesh-
|
|
1974
|
+
//# sourceMappingURL=splat-mesh-B1KlWHvx.js.map
|