@voluma/vlam 0.2.4 → 0.3.2
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 +3 -4
- package/dist/chunk-loader-DlNwggfG.js +1859 -0
- package/dist/chunk-loader-DlNwggfG.js.map +1 -0
- package/dist/{compute-sorter.d.ts → core/compute-sorter.d.ts} +1 -1
- package/dist/core/index.d.ts +45 -0
- package/dist/{splat-scene.d.ts → core/merged-splat-mesh.d.ts} +14 -15
- package/dist/{orientation.d.ts → core/orientation.d.ts} +7 -7
- package/dist/{relighting.d.ts → core/relighting.d.ts} +1 -1
- package/dist/{source-transform.d.ts → core/source-transform.d.ts} +4 -4
- package/dist/{splat-budget.d.ts → core/splat-budget.d.ts} +2 -2
- package/dist/{splat-data.d.ts → core/splat-data.d.ts} +4 -4
- package/dist/{splat-mesh-material.d.ts → core/splat-mesh-material.d.ts} +2 -2
- package/dist/{splat-mesh-types.d.ts → core/splat-mesh-types.d.ts} +19 -10
- package/dist/{splat-mesh.d.ts → core/splat-mesh.d.ts} +4 -4
- package/dist/{splat-modifier.d.ts → core/splat-modifier.d.ts} +5 -5
- package/dist/{webgpu-limits.d.ts → core/webgpu-limits.d.ts} +0 -59
- package/dist/{effects.d.ts → effects/index.d.ts} +14 -13
- package/dist/effects.js.map +1 -1
- package/dist/formats/ksplat/index.d.ts +1 -1
- package/dist/formats/ksplat/parse-ksplat.d.ts +1 -1
- package/dist/formats/ksplat.js.map +1 -1
- package/dist/formats/lcc/collision-mesh.d.ts +3 -3
- package/dist/formats/lcc/collision-partition.d.ts +1 -1
- package/dist/formats/lcc/lcc.d.ts +3 -3
- package/dist/formats/lcc/lcc2.d.ts +2 -2
- package/dist/formats/lcc/parse-lcc.d.ts +1 -1
- package/dist/formats/lcc.js +17 -16
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/ply/index.d.ts +1 -1
- package/dist/formats/ply/parse-compressed-ply.d.ts +2 -2
- package/dist/formats/ply/parse-splat-ply.d.ts +4 -4
- package/dist/formats/ply.js +1 -1
- package/dist/formats/ply.js.map +1 -1
- package/dist/formats/rad/frontier-worker-protocol.d.ts +8 -1
- package/dist/formats/rad/index.d.ts +2 -2
- package/dist/formats/rad/parse-rad.d.ts +2 -2
- package/dist/formats/rad/rad-foveated-source.d.ts +3 -3
- package/dist/formats/rad/rad.d.ts +7 -7
- package/dist/formats/rad.js +3 -3
- package/dist/formats/rad.js.map +1 -1
- package/dist/formats/sog/index.d.ts +1 -1
- package/dist/formats/sog/parse-sog.d.ts +6 -6
- package/dist/formats/sog.js.map +1 -1
- package/dist/formats/splat/index.d.ts +1 -1
- package/dist/formats/splat/parse-splat.d.ts +1 -1
- package/dist/formats/splat.js.map +1 -1
- package/dist/formats/spz/index.d.ts +1 -1
- package/dist/formats/spz/parse-spz.d.ts +1 -1
- package/dist/formats/spz.js.map +1 -1
- package/dist/half-float-Cs4FDq8Q.js.map +1 -1
- package/dist/index.js +253 -9047
- package/dist/index.js.map +1 -1
- package/dist/{chunk-loader.d.ts → loaders/chunk-loader.d.ts} +4 -4
- package/dist/loaders/index.d.ts +15 -0
- package/dist/{load-scene.d.ts → loaders/load-splat-data.d.ts} +16 -14
- package/dist/{load-worker-protocol.d.ts → loaders/load-worker-protocol.d.ts} +2 -2
- package/dist/{loading.d.ts → loaders/loading.d.ts} +8 -8
- package/dist/loaders.js +26 -0
- package/dist/loaders.js.map +1 -0
- package/dist/loading-wLo8vRbA.js.map +1 -1
- package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
- package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
- package/dist/logging-BfPdd7NJ.js.map +1 -1
- package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
- package/dist/ply-header-B9WllbPg.js.map +1 -0
- package/dist/radix-sorter-BrbUg_CV.js +178 -0
- package/dist/radix-sorter-BrbUg_CV.js.map +1 -0
- package/dist/relighting-Tiwep8yd.js +977 -0
- package/dist/relighting-Tiwep8yd.js.map +1 -0
- package/dist/selection/index.d.ts +13 -0
- package/dist/{selection-volume.d.ts → selection/selection-volume.d.ts} +1 -1
- package/dist/{splat-partition.d.ts → selection/splat-partition.d.ts} +2 -2
- package/dist/selection.js +137 -0
- package/dist/selection.js.map +1 -0
- package/dist/sh-pack-D5wAe5gg.js.map +1 -1
- package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
- package/dist/splat-budget-DuVZspPQ.js.map +1 -0
- package/dist/splat-data-BZM5dhhx.js.map +1 -1
- package/dist/splat-mesh-CsLOQb08.js +1942 -0
- package/dist/splat-mesh-CsLOQb08.js.map +1 -0
- package/dist/splat-mesh-types-BZIko-_9.js +18 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
- package/dist/static-lod/index.d.ts +12 -0
- package/dist/{static-lod-splat-mesh.d.ts → static-lod/static-lod-splat-mesh.d.ts} +3 -3
- package/dist/{static-lod-worker-protocol.d.ts → static-lod/static-lod-worker-protocol.d.ts} +1 -1
- package/dist/{static-lod.d.ts → static-lod/static-lod.d.ts} +1 -1
- package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
- package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
- package/dist/static-lod.js +148 -0
- package/dist/static-lod.js.map +1 -0
- package/dist/{budget-governor.d.ts → streaming/budget-governor.d.ts} +4 -4
- package/dist/{camera-budget-governor.d.ts → streaming/camera-budget-governor.d.ts} +11 -11
- package/dist/{chunk-cache-budget.d.ts → streaming/chunk-cache-budget.d.ts} +5 -5
- package/dist/{chunk-fetch-scheduler.d.ts → streaming/chunk-fetch-scheduler.d.ts} +4 -4
- package/dist/{dataset-source.d.ts → streaming/dataset-source.d.ts} +1 -1
- package/dist/streaming/index.d.ts +23 -0
- package/dist/{lod-scheduler.d.ts → streaming/lod-scheduler.d.ts} +1 -1
- package/dist/{lod-source.d.ts → streaming/lod-source.d.ts} +9 -9
- package/dist/{streamed-splat-mesh-utils.d.ts → streaming/streamed-splat-mesh-utils.d.ts} +2 -2
- package/dist/{streamed-splat-mesh.d.ts → streaming/streamed-splat-mesh.d.ts} +66 -32
- package/dist/streaming.js +2911 -0
- package/dist/streaming.js.map +1 -0
- package/dist/unified/index.d.ts +11 -0
- package/dist/{unified-splat-renderer.d.ts → unified/unified-splat-mesh.d.ts} +23 -16
- package/dist/unified/unified-work-buffer.d.ts +66 -0
- package/dist/{work-buffer-gather.d.ts → unified/work-buffer-gather.d.ts} +6 -6
- package/dist/{work-buffer-material.d.ts → unified/work-buffer-material.d.ts} +1 -1
- package/dist/unified.js +746 -0
- package/dist/unified.js.map +1 -0
- package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
- package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
- package/package.json +27 -7
- package/dist/index.d.ts +0 -54
- package/dist/ply-header-BwgExCn-.js.map +0 -1
- package/dist/splat-budget-DXew_86c.js.map +0 -1
- package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
- /package/dist/{create-splat-renderer.d.ts → core/create-splat-renderer.d.ts} +0 -0
- /package/dist/{depth-of-field.d.ts → core/depth-of-field.d.ts} +0 -0
- /package/dist/{half-float.d.ts → core/half-float.d.ts} +0 -0
- /package/dist/{logging.d.ts → core/logging.d.ts} +0 -0
- /package/dist/{radix-sort.d.ts → core/radix-sort.d.ts} +0 -0
- /package/dist/{radix-sorter.d.ts → core/radix-sorter.d.ts} +0 -0
- /package/dist/{relighting-pass.d.ts → core/relighting-pass.d.ts} +0 -0
- /package/dist/{sh-pack.d.ts → core/sh-pack.d.ts} +0 -0
- /package/dist/{sort-scheduler.d.ts → core/sort-scheduler.d.ts} +0 -0
- /package/dist/{sort-worker-protocol.d.ts → core/sort-worker-protocol.d.ts} +0 -0
- /package/dist/{sorter.d.ts → core/sorter.d.ts} +0 -0
- /package/dist/{splat-depth-pack.d.ts → core/splat-depth-pack.d.ts} +0 -0
- /package/dist/{splat-mesh-picking.d.ts → core/splat-mesh-picking.d.ts} +0 -0
- /package/dist/{splat-mesh-pool.d.ts → core/splat-mesh-pool.d.ts} +0 -0
- /package/dist/{splat-modifier-stack.d.ts → core/splat-modifier-stack.d.ts} +0 -0
- /package/dist/{splat-query.d.ts → core/splat-query.d.ts} +0 -0
- /package/dist/{splat-sort-bounds.d.ts → core/splat-sort-bounds.d.ts} +0 -0
- /package/dist/{storage-attribute-mirror.d.ts → core/storage-attribute-mirror.d.ts} +0 -0
- /package/dist/{worker-sorter.d.ts → core/worker-sorter.d.ts} +0 -0
- /package/dist/{xr-view.d.ts → core/xr-view.d.ts} +0 -0
- /package/dist/{ply-header.d.ts → loaders/ply-header.d.ts} +0 -0
- /package/dist/{lod-manifest.d.ts → streaming/lod-manifest.d.ts} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voluma/vlam/static-lod` - worker-built merged hierarchy for a fully
|
|
3
|
+
* decoded capture.
|
|
4
|
+
*
|
|
5
|
+
* {@link StaticLodSplatMesh.load} fetches through `@voluma/vlam/loaders` and
|
|
6
|
+
* then builds the hierarchy off the main thread. Import this entry rather
|
|
7
|
+
* than calling a streaming alias: there is none.
|
|
8
|
+
*
|
|
9
|
+
* @module static-lod
|
|
10
|
+
*/
|
|
11
|
+
export { StaticLodSplatMesh, type StaticLodSplatMeshOptions, type StaticLodSplatMeshLoadOptions, type StaticLodLoadProgress, } from './static-lod-splat-mesh';
|
|
12
|
+
export type { StaticLodBuildProgress } from './static-lod';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @role Bridge - Camera-aware rendering for worker-built static splat hierarchies. */
|
|
2
2
|
import * as THREE from 'three/webgpu';
|
|
3
|
-
import { type
|
|
4
|
-
import { SplatMesh, type SplatMeshOptions, type SplatUpdateOptions } from '
|
|
3
|
+
import { type SplatDataLoadOptions } from '../loaders/load-splat-data';
|
|
4
|
+
import { SplatMesh, type SplatMeshOptions, type SplatUpdateOptions } from '../core/splat-mesh';
|
|
5
5
|
import type { StaticLodBuildProgress } from './static-lod';
|
|
6
6
|
export interface StaticLodSplatMeshOptions extends SplatMeshOptions {
|
|
7
7
|
/** Initial selected-frontier budget. Defaults to the build ceiling. */
|
|
@@ -18,7 +18,7 @@ export interface StaticLodLoadProgress {
|
|
|
18
18
|
readonly completed: number;
|
|
19
19
|
readonly total: number;
|
|
20
20
|
}
|
|
21
|
-
export interface StaticLodSplatMeshLoadOptions extends StaticLodSplatMeshOptions,
|
|
21
|
+
export interface StaticLodSplatMeshLoadOptions extends StaticLodSplatMeshOptions, SplatDataLoadOptions {
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Static splat mesh backed by a merged hierarchy and camera-aware frontier.
|
|
@@ -18,14 +18,14 @@ function rt(t, n, o = lt) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
21
|
-
const c = /* @__PURE__ */ new Map(),
|
|
22
|
-
const
|
|
23
|
-
return !m || !m.radTree ? null : { data: m, local: y -
|
|
24
|
-
}, D = (y,
|
|
21
|
+
const c = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), l = new ft(), p = s.origin.x, h = s.origin.y, f = s.origin.z, r = s.forward, a = (r == null ? void 0 : r.x) ?? 0, M = (r == null ? void 0 : r.y) ?? 0, b = (r == null ? void 0 : r.z) ?? 0, N = r !== void 0, { coneDot0: S, coneDot: v, coneFoveate: F, behindFoveate: A } = s, R = (y) => {
|
|
22
|
+
const u = Math.floor(y / o), m = t.get(u);
|
|
23
|
+
return !m || !m.radTree ? null : { data: m, local: y - u * o };
|
|
24
|
+
}, D = (y, u) => {
|
|
25
25
|
let m = c.get(y);
|
|
26
|
-
m || (m = [], c.set(y, m)), m.push(
|
|
27
|
-
}, V = (y,
|
|
28
|
-
const m = y.positions, P =
|
|
26
|
+
m || (m = [], c.set(y, m)), m.push(u);
|
|
27
|
+
}, V = (y, u) => {
|
|
28
|
+
const m = y.positions, P = u * 3, E = m[P] - p, C = m[P + 1] - h, O = m[P + 2] - f, x = Math.sqrt(E * E + C * C + O * O) || 1e-6, U = y.radTree.size[u] / x;
|
|
29
29
|
if (!N) return U;
|
|
30
30
|
const I = E * a + C * M + O * b;
|
|
31
31
|
if (I <= 0) return U * A;
|
|
@@ -41,19 +41,19 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
41
41
|
const $ = /* @__PURE__ */ new Set();
|
|
42
42
|
for (const y of n) {
|
|
43
43
|
if ($.has(y)) continue;
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const u = R(y);
|
|
45
|
+
u && ($.add(y), l.push(y, V(u.data, u.local)), _++);
|
|
46
46
|
}
|
|
47
47
|
let k = !1;
|
|
48
48
|
for (; l.size > 0; ) {
|
|
49
49
|
const y = l.peekPriority();
|
|
50
50
|
if (y <= e) break;
|
|
51
|
-
const
|
|
51
|
+
const u = l.peek(), m = R(u);
|
|
52
52
|
if (!m) {
|
|
53
53
|
l.pop(), _--;
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
|
-
const P = Math.floor(
|
|
56
|
+
const P = Math.floor(u / o), E = m.data.radTree, C = E.childCount[m.local];
|
|
57
57
|
if (C === 0) {
|
|
58
58
|
l.pop(), D(P, m.local);
|
|
59
59
|
continue;
|
|
@@ -67,7 +67,7 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
67
67
|
const x = E.childStart[m.local], U = Math.floor(x / o), I = Math.floor((x + C - 1) / o);
|
|
68
68
|
let T = !0;
|
|
69
69
|
for (let L = U; L <= I; L++)
|
|
70
|
-
t.has(L) || (T = !1, y > (
|
|
70
|
+
t.has(L) || (T = !1, y > (d.get(L) ?? 0) && d.set(L, y));
|
|
71
71
|
if (!T) {
|
|
72
72
|
D(P, m.local);
|
|
73
73
|
continue;
|
|
@@ -80,9 +80,9 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
80
80
|
}
|
|
81
81
|
let w = !1;
|
|
82
82
|
for (; l.size > 0; ) {
|
|
83
|
-
const y = l.pop(),
|
|
84
|
-
if (D(
|
|
85
|
-
const P = t.get(
|
|
83
|
+
const y = l.pop(), u = Math.floor(y / o), m = y - u * o;
|
|
84
|
+
if (D(u, m), !w) {
|
|
85
|
+
const P = t.get(u), E = P == null ? void 0 : P.radTree, C = E ? E.childCount[m] : 0;
|
|
86
86
|
if (C > 0) {
|
|
87
87
|
const O = E.childStart[m], x = Math.floor(O / o), U = Math.floor((O + C - 1) / o);
|
|
88
88
|
let I = !0;
|
|
@@ -94,9 +94,9 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
94
94
|
}
|
|
95
95
|
const G = /* @__PURE__ */ new Map();
|
|
96
96
|
let Y = 0;
|
|
97
|
-
for (const [y,
|
|
98
|
-
G.set(y, Uint32Array.from(
|
|
99
|
-
return { selection: G, count: Y, touched:
|
|
97
|
+
for (const [y, u] of c)
|
|
98
|
+
G.set(y, Uint32Array.from(u)), Y += u.length;
|
|
99
|
+
return { selection: G, count: Y, touched: d, budgetClamped: k, refinable: w };
|
|
100
100
|
}
|
|
101
101
|
class ft {
|
|
102
102
|
constructor() {
|
|
@@ -129,13 +129,13 @@ class ft {
|
|
|
129
129
|
if (n.length > 0) {
|
|
130
130
|
n[0] = e, o[0] = i;
|
|
131
131
|
const c = n.length;
|
|
132
|
-
let
|
|
132
|
+
let d = 0;
|
|
133
133
|
for (; ; ) {
|
|
134
|
-
const l = 2 *
|
|
135
|
-
let h =
|
|
136
|
-
if (l < c && o[l] > o[h] && (h = l), p < c && o[p] > o[h] && (h = p), h ===
|
|
137
|
-
const f = n[
|
|
138
|
-
n[
|
|
134
|
+
const l = 2 * d + 1, p = l + 1;
|
|
135
|
+
let h = d;
|
|
136
|
+
if (l < c && o[l] > o[h] && (h = l), p < c && o[p] > o[h] && (h = p), h === d) break;
|
|
137
|
+
const f = n[d], r = o[d];
|
|
138
|
+
n[d] = n[h], o[d] = o[h], n[h] = f, o[h] = r, d = h;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
return s;
|
|
@@ -145,8 +145,8 @@ function nt(t) {
|
|
|
145
145
|
return [0, 3, 8, 15][t] ?? 0;
|
|
146
146
|
}
|
|
147
147
|
const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
148
|
-
const o = n * 6, s = t[o], e = t[o + 1], i = t[o + 2], c = t[o + 3],
|
|
149
|
-
return s * (c * l -
|
|
148
|
+
const o = n * 6, s = t[o], e = t[o + 1], i = t[o + 2], c = t[o + 3], d = t[o + 4], l = t[o + 5];
|
|
149
|
+
return s * (c * l - d * d) - e * (e * l - d * i) + i * (e * d - c * i);
|
|
150
150
|
}, pt = (t, n, o) => {
|
|
151
151
|
const s = t[o * 4 + 3] / 255, e = Math.sqrt(Math.max(q, st(n, o)));
|
|
152
152
|
return Math.max(q, s * e);
|
|
@@ -156,7 +156,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
156
156
|
}, W = (t) => {
|
|
157
157
|
let n = t & 1023;
|
|
158
158
|
return n = (n | n << 16) & 50331903, n = (n | n << 8) & 50393103, n = (n | n << 4) & 51130563, n = (n | n << 2) & 153391689, n >>> 0;
|
|
159
|
-
},
|
|
159
|
+
}, dt = (t, n, o) => (W(t) | W(n) << 1 | W(o) << 2) >>> 0, ut = (t, n) => {
|
|
160
160
|
const o = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], s = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
|
161
161
|
for (let p = 0; p < n; p++) {
|
|
162
162
|
const h = p * 3;
|
|
@@ -165,7 +165,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
165
165
|
o[f] = Math.min(o[f], r), s[f] = Math.max(s[f], r);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
let e = new Uint32Array(n), i = new Uint32Array(n), c = new Uint32Array(n),
|
|
168
|
+
let e = new Uint32Array(n), i = new Uint32Array(n), c = new Uint32Array(n), d = new Uint32Array(n);
|
|
169
169
|
const l = o.map(
|
|
170
170
|
(p, h) => (z - 1) / Math.max(1e-12, s[h] - p)
|
|
171
171
|
);
|
|
@@ -189,7 +189,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
189
189
|
Math.floor((t[h + 2] - o[2]) * l[2])
|
|
190
190
|
)
|
|
191
191
|
);
|
|
192
|
-
e[p] = p, c[p] =
|
|
192
|
+
e[p] = p, c[p] = dt(f, r, a);
|
|
193
193
|
}
|
|
194
194
|
for (let p = 0; p < 30; p += ot) {
|
|
195
195
|
const h = new Uint32Array(z);
|
|
@@ -204,9 +204,9 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
204
204
|
}
|
|
205
205
|
for (let r = 0; r < n; r++) {
|
|
206
206
|
const a = c[r], M = a >>> p & z - 1, b = h[M];
|
|
207
|
-
h[M] = b + 1,
|
|
207
|
+
h[M] = b + 1, d[b] = a, i[b] = e[r];
|
|
208
208
|
}
|
|
209
|
-
[c,
|
|
209
|
+
[c, d] = [d, c], [e, i] = [i, e];
|
|
210
210
|
}
|
|
211
211
|
return e;
|
|
212
212
|
};
|
|
@@ -261,11 +261,11 @@ const B = (t) => t.shPacked ? nt(t.shPacked.bands) : 0, mt = (t, n) => ({
|
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
}, ct = (t, n, o, s, e) => {
|
|
264
|
-
let i = 0, c = n,
|
|
264
|
+
let i = 0, c = n, d = -1;
|
|
265
265
|
const l = [0, 0, 0], p = [0, 0, 0];
|
|
266
266
|
for (let a = n; a < o; a++) {
|
|
267
267
|
const M = t.masses[a];
|
|
268
|
-
i += M, M >
|
|
268
|
+
i += M, M > d && (d = M, c = a);
|
|
269
269
|
for (let b = 0; b < 3; b++)
|
|
270
270
|
l[b] = l[b] + t.positions[a * 3 + b] * M, p[b] = p[b] + t.colors[a * 4 + b] * M;
|
|
271
271
|
}
|
|
@@ -305,8 +305,8 @@ const B = (t) => t.shPacked ? nt(t.shPacked.bands) : 0, mt = (t, n) => ({
|
|
|
305
305
|
}
|
|
306
306
|
const s = Math.ceil(t.count / o.count), e = mt(s, t);
|
|
307
307
|
for (let i = 0; i < o.count; i++) {
|
|
308
|
-
const c = Math.floor(i * t.count / o.count),
|
|
309
|
-
e.count =
|
|
308
|
+
const c = Math.floor(i * t.count / o.count), d = Math.max(c + 1, Math.floor((i + 1) * t.count / o.count));
|
|
309
|
+
e.count = d - c;
|
|
310
310
|
for (let l = 0; l < e.count; l++)
|
|
311
311
|
g(t, n[c + l], e, l);
|
|
312
312
|
ct(e, 0, e.count, o, i);
|
|
@@ -323,17 +323,17 @@ function Mt(t, n, o) {
|
|
|
323
323
|
return { data: t, contentSplatCount: 0, finestSplatCount: 0, roots: new Uint32Array() };
|
|
324
324
|
const s = Math.max(1, Math.min(t.count, Math.floor(n)));
|
|
325
325
|
o == null || o({ completed: 0, total: t.count });
|
|
326
|
-
const e =
|
|
326
|
+
const e = ut(t.positions, t.count), i = [s];
|
|
327
327
|
for (; (i.at(-1) ?? 0) > 1; )
|
|
328
328
|
i.push(Math.ceil(i.at(-1) / 2));
|
|
329
|
-
const c = i.reduce((k, w) => k + w, 0),
|
|
329
|
+
const c = i.reduce((k, w) => k + w, 0), d = new Float32Array(c * 3), l = new Uint8Array(c * 4), p = new Float32Array(c * 6), h = new Float64Array(c), f = new Uint32Array(c), r = new Uint16Array(c), a = new Uint32Array(c), M = new Float32Array(c), b = t.sh ? new Uint32Array(c) : void 0, N = B(t), S = t.shPacked ? new Uint32Array(c * N) : void 0, v = [];
|
|
330
330
|
let F = 0;
|
|
331
331
|
const A = i.map((k) => {
|
|
332
332
|
v.push(F);
|
|
333
333
|
const w = F;
|
|
334
334
|
return F += k, {
|
|
335
335
|
count: k,
|
|
336
|
-
positions:
|
|
336
|
+
positions: d.subarray(w * 3, F * 3),
|
|
337
337
|
colors: l.subarray(w * 4, F * 4),
|
|
338
338
|
covariances: p.subarray(w * 6, F * 6),
|
|
339
339
|
masses: h.subarray(w, F),
|
|
@@ -351,20 +351,20 @@ function Mt(t, n, o) {
|
|
|
351
351
|
yt(A[k - 1], A[k]);
|
|
352
352
|
for (let k = 1; k < A.length; k++) {
|
|
353
353
|
const w = A[k], G = A[k - 1], Y = v[k], y = v[k - 1];
|
|
354
|
-
for (let
|
|
355
|
-
const m = Math.min(2, G.count -
|
|
356
|
-
r[Y +
|
|
354
|
+
for (let u = 0; u < w.count; u++) {
|
|
355
|
+
const m = Math.min(2, G.count - u * 2);
|
|
356
|
+
r[Y + u] = m, a[Y + u] = y + u * 2;
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
const R = { childCount: r, childStart: a, size: M }, D = t.sh && b ? { ...t.sh, labels: b } : void 0, V = t.shPacked && S ? { ...t.shPacked, packed: S } : void 0, _ = {
|
|
360
360
|
count: c,
|
|
361
|
-
positions:
|
|
361
|
+
positions: d,
|
|
362
362
|
colors: l,
|
|
363
363
|
covariances: p,
|
|
364
364
|
...D ? { sh: D } : {},
|
|
365
365
|
...V ? { shPacked: V } : {},
|
|
366
366
|
...t.antialias === void 0 ? {} : { antialias: t.antialias },
|
|
367
|
-
...t.
|
|
367
|
+
...t.format === void 0 ? {} : { format: t.format },
|
|
368
368
|
radTree: R
|
|
369
369
|
};
|
|
370
370
|
return o == null || o({ completed: t.count, total: t.count }), {
|
|
@@ -383,7 +383,7 @@ const kt = (t) => (J(t), {
|
|
|
383
383
|
...t.sh ? { sh: { ...t.sh, labels: t.sh.labels.slice() } } : {},
|
|
384
384
|
...t.shPacked ? { shPacked: { ...t.shPacked, packed: t.shPacked.packed.slice() } } : {},
|
|
385
385
|
...t.antialias === void 0 ? {} : { antialias: t.antialias },
|
|
386
|
-
...t.
|
|
386
|
+
...t.format === void 0 ? {} : { format: t.format }
|
|
387
387
|
});
|
|
388
388
|
function wt(t, n) {
|
|
389
389
|
try {
|
|
@@ -445,7 +445,7 @@ self.onmessage = (t) => {
|
|
|
445
445
|
j(n);
|
|
446
446
|
});
|
|
447
447
|
};
|
|
448
|
-
//# sourceMappingURL=static-lod-worker-
|
|
448
|
+
//# sourceMappingURL=static-lod-worker-CjrXUR8x.js.map
|
|
449
449
|
`, o = typeof self < "u" && self.Blob && new Blob(["URL.revokeObjectURL(import.meta.url);", a], { type: "text/javascript;charset=utf-8" });
|
|
450
450
|
function r(n) {
|
|
451
451
|
let t;
|
|
@@ -471,4 +471,4 @@ function r(n) {
|
|
|
471
471
|
export {
|
|
472
472
|
r as default
|
|
473
473
|
};
|
|
474
|
-
//# sourceMappingURL=static-lod-worker-
|
|
474
|
+
//# sourceMappingURL=static-lod-worker-DktKnJkR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-lod-worker-
|
|
1
|
+
{"version":3,"file":"static-lod-worker-DktKnJkR.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var V = (s, r, e) => r in s ? S(s, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[r] = e;
|
|
3
|
+
var i = (s, r, e) => V(s, typeof r != "symbol" ? r + "" : r, e);
|
|
4
|
+
import * as g from "three/webgpu";
|
|
5
|
+
import { loadSplatData as L } from "./loaders.js";
|
|
6
|
+
import { S as P } from "./splat-mesh-CsLOQb08.js";
|
|
7
|
+
const b = 4096, A = 250, x = (s) => {
|
|
8
|
+
const r = [
|
|
9
|
+
s.positions.buffer,
|
|
10
|
+
s.colors.buffer,
|
|
11
|
+
s.covariances.buffer
|
|
12
|
+
];
|
|
13
|
+
return s.sh && r.push(s.sh.labels.buffer), s.shPacked && r.push(s.shPacked.packed.buffer), r;
|
|
14
|
+
}, w = (s, r) => {
|
|
15
|
+
if (!Number.isFinite(s) || s <= 0)
|
|
16
|
+
throw new RangeError("StaticLodSplatMesh budget must be a positive finite number.");
|
|
17
|
+
const e = Math.min(r, Math.floor(s));
|
|
18
|
+
return e === r || e < b ? Math.max(1, e) : Math.max(
|
|
19
|
+
b,
|
|
20
|
+
Math.floor(e / b) * b
|
|
21
|
+
);
|
|
22
|
+
}, E = async (s, r, e) => {
|
|
23
|
+
const n = (await import("./static-lod-worker-DktKnJkR.js")).default, o = new n();
|
|
24
|
+
return await new Promise((c, d) => {
|
|
25
|
+
const a = () => {
|
|
26
|
+
o.terminate(), d(new DOMException("Static LOD build aborted.", "AbortError"));
|
|
27
|
+
};
|
|
28
|
+
if (e != null && e.aborted) {
|
|
29
|
+
a();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
e == null || e.addEventListener("abort", a, { once: !0 }), o.onerror = (l) => {
|
|
33
|
+
e == null || e.removeEventListener("abort", a), o.terminate(), d(new Error(l.message));
|
|
34
|
+
}, o.onmessage = (l) => {
|
|
35
|
+
var f, m;
|
|
36
|
+
const u = l.data;
|
|
37
|
+
if (u.type === "progress") {
|
|
38
|
+
(f = r.onLodProgress) == null || f.call(r, u.progress), (m = r.onAutoLodProgress) == null || m.call(r, { phase: "lod-build", ...u.progress });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (u.type === "error") {
|
|
42
|
+
e == null || e.removeEventListener("abort", a), o.terminate(), d(new Error(u.message));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
u.type === "built" && (e == null || e.removeEventListener("abort", a), c({
|
|
46
|
+
data: u.data,
|
|
47
|
+
contentSplatCount: u.contentSplatCount,
|
|
48
|
+
finestSplatCount: u.finestSplatCount,
|
|
49
|
+
worker: o
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
52
|
+
const h = {
|
|
53
|
+
type: "build",
|
|
54
|
+
source: s,
|
|
55
|
+
maxBudget: r.maxBudget
|
|
56
|
+
};
|
|
57
|
+
o.postMessage(h, x(s));
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
class p extends P {
|
|
61
|
+
constructor(e, t, n, o, c) {
|
|
62
|
+
super(e, c);
|
|
63
|
+
i(this, "worker");
|
|
64
|
+
i(this, "ceilingValue");
|
|
65
|
+
i(this, "contentCountValue");
|
|
66
|
+
i(this, "budgetValue");
|
|
67
|
+
i(this, "sequence", 0);
|
|
68
|
+
i(this, "lastScheduleAt", -1 / 0);
|
|
69
|
+
i(this, "selectionPending", !1);
|
|
70
|
+
i(this, "cameraLocal", new g.Vector3());
|
|
71
|
+
i(this, "cameraForward", new g.Vector3(0, 0, -1));
|
|
72
|
+
i(this, "inverseWorld", new g.Matrix4());
|
|
73
|
+
this.worker = o, this.ceilingValue = n, this.contentCountValue = t, this.budgetValue = w(c.budget ?? this.ceilingValue, this.ceilingValue), this.replaceActivePrefix(this.ceilingValue), this.worker.onmessage = (d) => {
|
|
74
|
+
const a = d.data;
|
|
75
|
+
if (a.type === "error") {
|
|
76
|
+
this.selectionPending = !1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
a.type !== "selection" || a.sequence !== this.sequence || (this.selectionPending = !1, this.replaceActiveIndices(a.indices));
|
|
80
|
+
}, this.budgetValue < this.ceilingValue && this.scheduleSelection(!0);
|
|
81
|
+
}
|
|
82
|
+
/** Loads, decodes and builds a merged static hierarchy without blocking UI. */
|
|
83
|
+
static async load(e, t) {
|
|
84
|
+
var d, a;
|
|
85
|
+
let n = !1;
|
|
86
|
+
const o = await L(e, {
|
|
87
|
+
...t,
|
|
88
|
+
onProgress: (h, l) => {
|
|
89
|
+
var u, f, m;
|
|
90
|
+
(u = t.onProgress) == null || u.call(t, h, l), (f = t.onAutoLodProgress) == null || f.call(t, { phase: "download", completed: h, total: l }), !n && l > 0 && h >= l && (n = !0, (m = t.onAutoLodProgress) == null || m.call(t, { phase: "decode", completed: 0, total: 1 }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
n || (d = t.onAutoLodProgress) == null || d.call(t, { phase: "decode", completed: 0, total: 1 }), (a = t.onAutoLodProgress) == null || a.call(t, { phase: "decode", completed: 1, total: 1 });
|
|
94
|
+
const c = await E(o, t, t.signal);
|
|
95
|
+
return new p(
|
|
96
|
+
c.data,
|
|
97
|
+
c.contentSplatCount,
|
|
98
|
+
c.finestSplatCount,
|
|
99
|
+
c.worker,
|
|
100
|
+
t
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
get budget() {
|
|
104
|
+
return this.budgetValue;
|
|
105
|
+
}
|
|
106
|
+
get budgetCeiling() {
|
|
107
|
+
return this.ceilingValue;
|
|
108
|
+
}
|
|
109
|
+
get maxBudget() {
|
|
110
|
+
return this.ceilingValue;
|
|
111
|
+
}
|
|
112
|
+
get contentSplatCount() {
|
|
113
|
+
return this.contentCountValue;
|
|
114
|
+
}
|
|
115
|
+
/** Changes the selected frontier budget within the immutable build ceiling. */
|
|
116
|
+
setBudget(e) {
|
|
117
|
+
const t = w(e, this.ceilingValue);
|
|
118
|
+
return t === this.budgetValue || (this.budgetValue = t, this.selectionPending = !1, this.scheduleSelection(!0)), t;
|
|
119
|
+
}
|
|
120
|
+
update(e, t, n = {}) {
|
|
121
|
+
super.update(e, t, n), this.updateLod(e);
|
|
122
|
+
}
|
|
123
|
+
/** Schedules a camera-aware cut when a unified renderer owns drawing/sorting. */
|
|
124
|
+
updateLod(e) {
|
|
125
|
+
e.updateMatrixWorld(), this.updateWorldMatrix(!0, !1), this.inverseWorld.copy(this.matrixWorld).invert(), this.cameraLocal.setFromMatrixPosition(e.matrixWorld).applyMatrix4(this.inverseWorld), e.getWorldDirection(this.cameraForward).transformDirection(this.inverseWorld), this.scheduleSelection(!1);
|
|
126
|
+
}
|
|
127
|
+
dispose() {
|
|
128
|
+
this.disposed || (this.worker.terminate(), super.dispose());
|
|
129
|
+
}
|
|
130
|
+
scheduleSelection(e) {
|
|
131
|
+
if (this.disposed || this.selectionPending) return;
|
|
132
|
+
const t = performance.now();
|
|
133
|
+
if (!e && t - this.lastScheduleAt < A) return;
|
|
134
|
+
this.lastScheduleAt = t, this.selectionPending = !0;
|
|
135
|
+
const n = {
|
|
136
|
+
type: "select",
|
|
137
|
+
sequence: ++this.sequence,
|
|
138
|
+
budget: this.budgetValue,
|
|
139
|
+
cameraLocal: this.cameraLocal.toArray(),
|
|
140
|
+
cameraForward: this.cameraForward.toArray()
|
|
141
|
+
};
|
|
142
|
+
this.worker.postMessage(n);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
p as StaticLodSplatMesh
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=static-lod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-lod.js","sources":["../src/lib/static-lod/static-lod-splat-mesh.ts"],"sourcesContent":["/** @role Bridge - Camera-aware rendering for worker-built static splat hierarchies. */\nimport * as THREE from 'three/webgpu';\nimport { loadSplatData, type SplatDataLoadOptions } from '../loaders/load-splat-data';\nimport type { SplatData } from '../core/splat-data';\nimport { SplatMesh, type SplatMeshOptions, type SplatUpdateOptions } from '../core/splat-mesh';\nimport type { StaticLodBuildProgress } from './static-lod';\nimport type { StaticLodWorkerRequest, StaticLodWorkerResponse } from './static-lod-worker-protocol';\n\nconst BUDGET_GRANULARITY = 4096;\nconst CAMERA_RESCHEDULE_INTERVAL_MS = 250;\n\nexport interface StaticLodSplatMeshOptions extends SplatMeshOptions {\n /** Initial selected-frontier budget. Defaults to the build ceiling. */\n budget?: number;\n /** Finest frontier retained by the hierarchy and immutable pool ceiling. */\n maxBudget: number;\n /** Reports the spatial ordering and hierarchy-build phase. */\n onLodProgress?: (progress: StaticLodBuildProgress) => void;\n /** Phase-aware progress spanning download, decode completion and LOD build. */\n onAutoLodProgress?: (progress: StaticLodLoadProgress) => void;\n}\n\nexport interface StaticLodLoadProgress {\n readonly phase: 'download' | 'decode' | 'lod-build';\n readonly completed: number;\n readonly total: number;\n}\n\nexport interface StaticLodSplatMeshLoadOptions\n extends StaticLodSplatMeshOptions, SplatDataLoadOptions {}\n\nconst transferablesFor = (data: SplatData): Transferable[] => {\n const transferables: Transferable[] = [\n data.positions.buffer as ArrayBuffer,\n data.colors.buffer as ArrayBuffer,\n data.covariances.buffer as ArrayBuffer,\n ];\n if (data.sh) transferables.push(data.sh.labels.buffer as ArrayBuffer);\n if (data.shPacked) transferables.push(data.shPacked.packed.buffer as ArrayBuffer);\n return transferables;\n};\n\nconst normalizeBudget = (value: number, ceiling: number): number => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError('StaticLodSplatMesh budget must be a positive finite number.');\n }\n const clamped = Math.min(ceiling, Math.floor(value));\n if (clamped === ceiling || clamped < BUDGET_GRANULARITY) return Math.max(1, clamped);\n return Math.max(\n BUDGET_GRANULARITY,\n Math.floor(clamped / BUDGET_GRANULARITY) * BUDGET_GRANULARITY,\n );\n};\n\nconst buildInWorker = async (\n source: SplatData,\n options: StaticLodSplatMeshOptions,\n signal: AbortSignal | undefined,\n): Promise<{\n data: SplatData;\n contentSplatCount: number;\n finestSplatCount: number;\n worker: Worker;\n}> => {\n const module = await import('./static-lod-worker?worker&inline');\n const WorkerClass = module.default;\n const worker = new WorkerClass();\n return await new Promise((resolve, reject) => {\n const abort = (): void => {\n worker.terminate();\n reject(new DOMException('Static LOD build aborted.', 'AbortError'));\n };\n if (signal?.aborted) {\n abort();\n return;\n }\n signal?.addEventListener('abort', abort, { once: true });\n worker.onerror = (event): void => {\n signal?.removeEventListener('abort', abort);\n worker.terminate();\n reject(new Error(event.message));\n };\n worker.onmessage = (event: MessageEvent<StaticLodWorkerResponse>): void => {\n const response = event.data;\n if (response.type === 'progress') {\n options.onLodProgress?.(response.progress);\n options.onAutoLodProgress?.({ phase: 'lod-build', ...response.progress });\n return;\n }\n if (response.type === 'error') {\n signal?.removeEventListener('abort', abort);\n worker.terminate();\n reject(new Error(response.message));\n return;\n }\n if (response.type !== 'built') return;\n signal?.removeEventListener('abort', abort);\n resolve({\n data: response.data,\n contentSplatCount: response.contentSplatCount,\n finestSplatCount: response.finestSplatCount,\n worker,\n });\n };\n const request: StaticLodWorkerRequest = {\n type: 'build',\n source,\n maxBudget: options.maxBudget,\n };\n worker.postMessage(request, transferablesFor(source));\n });\n};\n\n/**\n * Static splat mesh backed by a merged hierarchy and camera-aware frontier.\n * The full hierarchy is uploaded once; camera/budget cuts only remap the\n * active pool indices and never rewrite resident splat attributes.\n */\nexport class StaticLodSplatMesh extends SplatMesh {\n private readonly worker: Worker;\n private readonly ceilingValue: number;\n private readonly contentCountValue: number;\n private budgetValue: number;\n private sequence = 0;\n private lastScheduleAt = -Infinity;\n private selectionPending = false;\n private readonly cameraLocal = new THREE.Vector3();\n private readonly cameraForward = new THREE.Vector3(0, 0, -1);\n private readonly inverseWorld = new THREE.Matrix4();\n\n private constructor(\n data: SplatData,\n contentSplatCount: number,\n finestSplatCount: number,\n worker: Worker,\n options: StaticLodSplatMeshOptions,\n ) {\n super(data, options);\n this.worker = worker;\n this.ceilingValue = finestSplatCount;\n this.contentCountValue = contentSplatCount;\n this.budgetValue = normalizeBudget(options.budget ?? this.ceilingValue, this.ceilingValue);\n // Hierarchy layout: finest level occupies pool indices `[0, finest)`.\n this.replaceActivePrefix(this.ceilingValue);\n this.worker.onmessage = (event: MessageEvent<StaticLodWorkerResponse>): void => {\n const response = event.data;\n if (response.type === 'error') {\n this.selectionPending = false;\n return;\n }\n if (response.type !== 'selection' || response.sequence !== this.sequence) return;\n this.selectionPending = false;\n this.replaceActiveIndices(response.indices);\n };\n if (this.budgetValue < this.ceilingValue) this.scheduleSelection(true);\n }\n\n /** Loads, decodes and builds a merged static hierarchy without blocking UI. */\n static async load(\n input: string | URL,\n options: StaticLodSplatMeshLoadOptions,\n ): Promise<StaticLodSplatMesh> {\n let decodeReported = false;\n const source = await loadSplatData(input, {\n ...options,\n onProgress: (completed, total) => {\n options.onProgress?.(completed, total);\n options.onAutoLodProgress?.({ phase: 'download', completed, total });\n if (!decodeReported && total > 0 && completed >= total) {\n decodeReported = true;\n options.onAutoLodProgress?.({ phase: 'decode', completed: 0, total: 1 });\n }\n },\n });\n if (!decodeReported) {\n options.onAutoLodProgress?.({ phase: 'decode', completed: 0, total: 1 });\n }\n options.onAutoLodProgress?.({ phase: 'decode', completed: 1, total: 1 });\n const result = await buildInWorker(source, options, options.signal);\n return new StaticLodSplatMesh(\n result.data,\n result.contentSplatCount,\n result.finestSplatCount,\n result.worker,\n options,\n );\n }\n\n get budget(): number {\n return this.budgetValue;\n }\n\n get budgetCeiling(): number {\n return this.ceilingValue;\n }\n\n get maxBudget(): number {\n return this.ceilingValue;\n }\n\n get contentSplatCount(): number {\n return this.contentCountValue;\n }\n\n /** Changes the selected frontier budget within the immutable build ceiling. */\n setBudget(value: number): number {\n const next = normalizeBudget(value, this.ceilingValue);\n if (next === this.budgetValue) return next;\n this.budgetValue = next;\n this.selectionPending = false;\n this.scheduleSelection(true);\n return next;\n }\n\n override update(\n camera: THREE.PerspectiveCamera,\n renderer: THREE.WebGPURenderer,\n options: SplatUpdateOptions = {},\n ): void {\n super.update(camera, renderer, options);\n this.updateLod(camera);\n }\n\n /** Schedules a camera-aware cut when a unified renderer owns drawing/sorting. */\n updateLod(camera: THREE.PerspectiveCamera): void {\n camera.updateMatrixWorld();\n this.updateWorldMatrix(true, false);\n this.inverseWorld.copy(this.matrixWorld).invert();\n this.cameraLocal.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(this.inverseWorld);\n camera.getWorldDirection(this.cameraForward).transformDirection(this.inverseWorld);\n this.scheduleSelection(false);\n }\n\n override dispose(): void {\n if (this.disposed) return;\n this.worker.terminate();\n super.dispose();\n }\n\n private scheduleSelection(immediate: boolean): void {\n if (this.disposed || this.selectionPending) return;\n const now = performance.now();\n if (!immediate && now - this.lastScheduleAt < CAMERA_RESCHEDULE_INTERVAL_MS) return;\n this.lastScheduleAt = now;\n this.selectionPending = true;\n const request: StaticLodWorkerRequest = {\n type: 'select',\n sequence: ++this.sequence,\n budget: this.budgetValue,\n cameraLocal: this.cameraLocal.toArray(),\n cameraForward: this.cameraForward.toArray(),\n };\n this.worker.postMessage(request);\n }\n}\n"],"names":["BUDGET_GRANULARITY","CAMERA_RESCHEDULE_INTERVAL_MS","transferablesFor","data","transferables","normalizeBudget","value","ceiling","clamped","buildInWorker","source","options","signal","WorkerClass","worker","resolve","reject","abort","event","response","_a","_b","request","StaticLodSplatMesh","SplatMesh","contentSplatCount","finestSplatCount","__publicField","THREE","input","decodeReported","loadSplatData","completed","total","_c","result","next","camera","renderer","immediate","now"],"mappings":"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"}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Shared splat-budget governance across multiple streamed meshes.
|
|
3
3
|
*
|
|
4
4
|
* A single `StreamedSplatMesh` keeps itself within a per-device budget, but a
|
|
5
|
-
* host that shows several streamed scenes at once (a main capture plus
|
|
5
|
+
* host that shows several streamed scenes at once (a main capture plus additional
|
|
6
6
|
* or inset meshes) must not let each mesh claim the whole device budget -
|
|
7
7
|
* their pools are separate, so the costs add. Historically hosts hand-tuned
|
|
8
|
-
* this ("shrink the main mesh to 0.7 when
|
|
8
|
+
* this ("shrink the main mesh to 0.7 when additional meshes exist"); the
|
|
9
9
|
* {@link BudgetGovernor} makes it a first-class policy: register each mesh
|
|
10
10
|
* with a priority weight and the governor splits one total budget across the
|
|
11
11
|
* members, reallocating when membership, weights, or the total change.
|
|
@@ -50,7 +50,7 @@ export interface BudgetGovernorOptions {
|
|
|
50
50
|
/**
|
|
51
51
|
* Grow dead-band as a fraction of a member's current budget (default
|
|
52
52
|
* `0.1`). A reallocation that would *raise* a member's budget by no more
|
|
53
|
-
* than this fraction is skipped, so brief membership churn (
|
|
53
|
+
* than this fraction is skipped, so brief membership churn (an additional mesh
|
|
54
54
|
* appearing for a moment) does not thrash LOD schedules. Shrinks always
|
|
55
55
|
* apply immediately - that is what keeps `sum(member budgets) ≤ total` an
|
|
56
56
|
* invariant rather than a goal.
|
|
@@ -98,7 +98,7 @@ export declare class BudgetGovernor {
|
|
|
98
98
|
*
|
|
99
99
|
* @param member - The mesh (or compatible object) to govern.
|
|
100
100
|
* @param options - `weight` (default `1`): the member's share is
|
|
101
|
-
* proportional to its weight - e.g. main mesh `7`,
|
|
101
|
+
* proportional to its weight - e.g. main mesh `7`, additional mesh `3` reproduces
|
|
102
102
|
* the old 0.7 host split. `0` registers the member suspended.
|
|
103
103
|
*/
|
|
104
104
|
register(member: BudgetGovernedMember, options?: {
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
* Camera-driven splat-budget weighting across several streamed meshes.
|
|
3
3
|
*
|
|
4
4
|
* {@link BudgetGovernor} splits one total by *priority*, which a host must
|
|
5
|
-
* choose and maintain. That is the wrong axis for a scene of
|
|
6
|
-
* makes a
|
|
7
|
-
* every frame. Splitting a pool evenly instead (`pool / N`) gives the
|
|
8
|
-
* you fly up to a quarter of the budget it needs while three
|
|
5
|
+
* choose and maintain. That is the wrong axis for a scene of additional meshes:
|
|
6
|
+
* what makes a mesh worth splats is that the camera is near it, and that changes
|
|
7
|
+
* every frame. Splitting a pool evenly instead (`pool / N`) gives the mesh
|
|
8
|
+
* you fly up to a quarter of the budget it needs while three meshes nobody is
|
|
9
9
|
* looking at hold the rest.
|
|
10
10
|
*
|
|
11
11
|
* {@link CameraBudgetGovernor} closes that loop: each update it measures every
|
|
12
12
|
* member's projected size from the camera, multiplies in the host's priority
|
|
13
13
|
* tier, and writes the resulting weights to a `BudgetGovernor` in one batch.
|
|
14
|
-
* Approaching one of four
|
|
14
|
+
* Approaching one of four additional meshes pulls budget off the far ones automatically.
|
|
15
15
|
*
|
|
16
16
|
* This is Spark's model, reached differently. Spark shares one `lodSplatCount`
|
|
17
17
|
* and biases meshes with a per-mesh `lodScale` (focused 2, adjacent 0.25,
|
|
@@ -53,7 +53,7 @@ export interface CameraBudgetMember extends BudgetGovernedMember {
|
|
|
53
53
|
/**
|
|
54
54
|
* The visibility that actually decides whether the member's splats reach the
|
|
55
55
|
* screen, when that differs from `visible`. `SplatMesh` provides it: a
|
|
56
|
-
* `
|
|
56
|
+
* `UnifiedSplatMesh` forces `visible = false` on every source it owns
|
|
57
57
|
* (only to keep the regular scene draw from double-drawing them) while the
|
|
58
58
|
* source may be fully on screen through the unified draw - without this, the
|
|
59
59
|
* governor would suspend every unified source and freeze its streaming.
|
|
@@ -117,8 +117,8 @@ export interface CameraBudgetMemberOptions {
|
|
|
117
117
|
/**
|
|
118
118
|
* Pins this member's weight, opting it out of camera weighting while it still
|
|
119
119
|
* competes for the same total. Use it for a main scene that should hold a
|
|
120
|
-
* steady share while
|
|
121
|
-
* against
|
|
120
|
+
* steady share while additional meshes fight over the rest - e.g. `fixedWeight: 4`
|
|
121
|
+
* against extras averaging `1`.
|
|
122
122
|
*/
|
|
123
123
|
fixedWeight?: number;
|
|
124
124
|
}
|
|
@@ -129,8 +129,8 @@ export interface CameraBudgetMemberOptions {
|
|
|
129
129
|
* ```js
|
|
130
130
|
* const governor = new CameraBudgetGovernor({ totalBudget: 4_000_000 });
|
|
131
131
|
* governor.register(main, { fixedWeight: 4 }); // steady share
|
|
132
|
-
* governor.register(
|
|
133
|
-
* governor.register(
|
|
132
|
+
* governor.register(extraA); // camera-weighted, priority 1
|
|
133
|
+
* governor.register(extraB);
|
|
134
134
|
*
|
|
135
135
|
* // once per frame, after the scene graph is up to date:
|
|
136
136
|
* governor.update(camera);
|
|
@@ -142,7 +142,7 @@ export interface CameraBudgetMemberOptions {
|
|
|
142
142
|
*
|
|
143
143
|
* **A member can only grow into a budget its pool can hold.** A streamed mesh
|
|
144
144
|
* allocates its pool once, from its construction budget, and clamps `setBudget`
|
|
145
|
-
* to it - so a
|
|
145
|
+
* to it - so a mesh built at a quarter of the total can never be given more
|
|
146
146
|
* than a quarter, however close the camera gets. Construct governed meshes with
|
|
147
147
|
* `maxBudget` set to the largest share they should ever reach, and price those
|
|
148
148
|
* ceilings with `estimateSplatPoolBytes` first: the pools cost their ceilings
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Cross-mesh decoded-chunk cache arbitration.
|
|
3
3
|
*
|
|
4
4
|
* Every {@link StreamedSplatMesh} caps its own decoded-chunk cache, and in
|
|
5
|
-
* `foveationMode: '
|
|
6
|
-
* size)` - a number sized for *one* streamed scene. A scene of streamed
|
|
7
|
-
* therefore has no ceiling at all: thirteen
|
|
5
|
+
* `foveationMode: 'page-table'` that cap is `min(2 GiB, this capture's decoded
|
|
6
|
+
* size)` - a number sized for *one* streamed scene. A scene of streamed additional meshes
|
|
7
|
+
* therefore has no ceiling at all: thirteen extras plus a main are thirteen
|
|
8
8
|
* plus one independent caps, and because each is sized to its own capture, a
|
|
9
9
|
* desktop that fits them never evicts. The background sweep then runs to
|
|
10
10
|
* completion against every one of them, pulling every capture in the scene into
|
|
@@ -43,8 +43,8 @@ export interface ChunkCacheClient {
|
|
|
43
43
|
*
|
|
44
44
|
* Bytes above it are handed to siblings that can use them, the way
|
|
45
45
|
* `BudgetGovernor` waterfills past a member's `maxBudget`. Without it a scene
|
|
46
|
-
* of one large capture and a dozen small
|
|
47
|
-
* envelope for
|
|
46
|
+
* of one large capture and a dozen small additional meshes would reserve most of the
|
|
47
|
+
* envelope for extras that cannot fill it.
|
|
48
48
|
*/
|
|
49
49
|
readonly ceilingBytes: number;
|
|
50
50
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every {@link StreamedSplatMesh} owns its own `ChunkLoader` worker and issues
|
|
5
5
|
* chunk requests toward its own in-flight cap, so a scene of streamed meshes
|
|
6
|
-
* fetches with no shared ordering: a
|
|
6
|
+
* fetches with no shared ordering: a mesh the camera is pointed at competes
|
|
7
7
|
* for bandwidth and connection slots against a dozen distant ones, each of
|
|
8
8
|
* which is equally entitled to its own cap. Spark does not have this problem
|
|
9
9
|
* structurally - one global traversal orders every fetch want
|
|
@@ -81,7 +81,7 @@ export interface ChunkFetchSchedulerOptions {
|
|
|
81
81
|
* Slots every registered mesh may hold regardless of weight. Default 1.
|
|
82
82
|
*
|
|
83
83
|
* Without a floor a scene's far meshes stop fetching entirely while the
|
|
84
|
-
* focused one streams, and a
|
|
84
|
+
* focused one streams, and a mesh with no coarse coverage draws nothing -
|
|
85
85
|
* so the floor is what turns "far meshes wait" into "far meshes trickle".
|
|
86
86
|
*
|
|
87
87
|
* One slot is always granted regardless of this setting: a mesh entitled to
|
|
@@ -156,8 +156,8 @@ export declare class ChunkFetchScheduler {
|
|
|
156
156
|
* asks - which is what keeps the first mesh to reschedule from taking the
|
|
157
157
|
* pipe and holding it. Dividing the *remainder* after every participant's
|
|
158
158
|
* floor instead would invert the whole point on a large scene: thirteen
|
|
159
|
-
*
|
|
160
|
-
* focused
|
|
159
|
+
* additional meshes and a main at floor 1 consume a 16-slot pipe entirely, leaving the
|
|
160
|
+
* focused mesh a smaller share than the far ones it is competing with.
|
|
161
161
|
*
|
|
162
162
|
* The denominator counts every mesh that plausibly wants the pipe - all
|
|
163
163
|
* registered meshes except those both weightless and idle, plus the caller.
|