@voluma/vlam 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -0
  3. package/THIRD_PARTY_NOTICES.md +86 -0
  4. package/dist/budget-governor.d.ts +154 -0
  5. package/dist/camera-budget-governor.d.ts +234 -0
  6. package/dist/chunk-cache-budget.d.ts +156 -0
  7. package/dist/chunk-fetch-scheduler.d.ts +168 -0
  8. package/dist/chunk-loader.d.ts +85 -0
  9. package/dist/compute-sorter.d.ts +131 -0
  10. package/dist/create-splat-renderer.d.ts +99 -0
  11. package/dist/dataset-source.d.ts +55 -0
  12. package/dist/depth-of-field.d.ts +73 -0
  13. package/dist/effects.d.ts +337 -0
  14. package/dist/effects.js +180 -0
  15. package/dist/effects.js.map +1 -0
  16. package/dist/formats/ksplat/index.d.ts +8 -0
  17. package/dist/formats/ksplat/parse-ksplat.d.ts +9 -0
  18. package/dist/formats/ksplat.js +167 -0
  19. package/dist/formats/ksplat.js.map +1 -0
  20. package/dist/formats/lcc/collision-mesh.d.ts +37 -0
  21. package/dist/formats/lcc/collision-partition.d.ts +35 -0
  22. package/dist/formats/lcc/index.d.ts +16 -0
  23. package/dist/formats/lcc/lcc.d.ts +28 -0
  24. package/dist/formats/lcc/lcc2-transform.d.ts +18 -0
  25. package/dist/formats/lcc/lcc2.d.ts +4 -0
  26. package/dist/formats/lcc/parse-collision-lci.d.ts +28 -0
  27. package/dist/formats/lcc/parse-lcc.d.ts +177 -0
  28. package/dist/formats/lcc/parse-mesh-ply.d.ts +21 -0
  29. package/dist/formats/lcc.js +969 -0
  30. package/dist/formats/lcc.js.map +1 -0
  31. package/dist/formats/ply/index.d.ts +12 -0
  32. package/dist/formats/ply/parse-compressed-ply.d.ts +16 -0
  33. package/dist/formats/ply/parse-splat-ply.d.ts +46 -0
  34. package/dist/formats/ply.js +230 -0
  35. package/dist/formats/ply.js.map +1 -0
  36. package/dist/formats/rad/frontier-worker-protocol.d.ts +177 -0
  37. package/dist/formats/rad/index.d.ts +10 -0
  38. package/dist/formats/rad/parse-rad.d.ts +88 -0
  39. package/dist/formats/rad/rad-column-decoders.d.ts +15 -0
  40. package/dist/formats/rad/rad-foveated-source.d.ts +71 -0
  41. package/dist/formats/rad/rad-parent-sizes.d.ts +45 -0
  42. package/dist/formats/rad/rad.d.ts +132 -0
  43. package/dist/formats/rad.js +1098 -0
  44. package/dist/formats/rad.js.map +1 -0
  45. package/dist/formats/sog/index.d.ts +9 -0
  46. package/dist/formats/sog/parse-sog.d.ts +45 -0
  47. package/dist/formats/sog.js +257 -0
  48. package/dist/formats/sog.js.map +1 -0
  49. package/dist/formats/splat/index.d.ts +8 -0
  50. package/dist/formats/splat/parse-splat.d.ts +10 -0
  51. package/dist/formats/splat.js +39 -0
  52. package/dist/formats/splat.js.map +1 -0
  53. package/dist/formats/spz/index.d.ts +8 -0
  54. package/dist/formats/spz/parse-spz.d.ts +10 -0
  55. package/dist/formats/spz.js +218 -0
  56. package/dist/formats/spz.js.map +1 -0
  57. package/dist/frontier-worker-CAnK_Cmu.js +566 -0
  58. package/dist/frontier-worker-CAnK_Cmu.js.map +1 -0
  59. package/dist/half-float-Cs4FDq8Q.js +30 -0
  60. package/dist/half-float-Cs4FDq8Q.js.map +1 -0
  61. package/dist/half-float.d.ts +15 -0
  62. package/dist/index.d.ts +54 -0
  63. package/dist/index.js +9054 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/load-scene.d.ts +50 -0
  66. package/dist/load-worker-protocol.d.ts +82 -0
  67. package/dist/loading-wLo8vRbA.js +102 -0
  68. package/dist/loading-wLo8vRbA.js.map +1 -0
  69. package/dist/loading.d.ts +150 -0
  70. package/dist/lod-manifest.d.ts +61 -0
  71. package/dist/lod-scheduler.d.ts +229 -0
  72. package/dist/lod-source.d.ts +207 -0
  73. package/dist/logging-BfPdd7NJ.js +19 -0
  74. package/dist/logging-BfPdd7NJ.js.map +1 -0
  75. package/dist/logging.d.ts +37 -0
  76. package/dist/one-shot-worker-G80btnth.js +664 -0
  77. package/dist/one-shot-worker-G80btnth.js.map +1 -0
  78. package/dist/orientation.d.ts +35 -0
  79. package/dist/ply-header-BwgExCn-.js +125 -0
  80. package/dist/ply-header-BwgExCn-.js.map +1 -0
  81. package/dist/ply-header.d.ts +87 -0
  82. package/dist/radix-sort.d.ts +28 -0
  83. package/dist/radix-sorter.d.ts +46 -0
  84. package/dist/relighting.d.ts +50 -0
  85. package/dist/selection-volume.d.ts +87 -0
  86. package/dist/sh-pack-D5wAe5gg.js +54 -0
  87. package/dist/sh-pack-D5wAe5gg.js.map +1 -0
  88. package/dist/sh-pack.d.ts +49 -0
  89. package/dist/sort-scheduler.d.ts +62 -0
  90. package/dist/sort-worker-protocol.d.ts +37 -0
  91. package/dist/sorter.d.ts +31 -0
  92. package/dist/source-transform.d.ts +57 -0
  93. package/dist/splat-budget-PSojLJPO.js +157 -0
  94. package/dist/splat-budget-PSojLJPO.js.map +1 -0
  95. package/dist/splat-budget.d.ts +404 -0
  96. package/dist/splat-data-BZM5dhhx.js +10 -0
  97. package/dist/splat-data-BZM5dhhx.js.map +1 -0
  98. package/dist/splat-data.d.ts +150 -0
  99. package/dist/splat-depth-pack.d.ts +39 -0
  100. package/dist/splat-mesh-material.d.ts +255 -0
  101. package/dist/splat-mesh-picking.d.ts +87 -0
  102. package/dist/splat-mesh-pool.d.ts +241 -0
  103. package/dist/splat-mesh-types-8QDjTbZO.js +390 -0
  104. package/dist/splat-mesh-types-8QDjTbZO.js.map +1 -0
  105. package/dist/splat-mesh-types.d.ts +390 -0
  106. package/dist/splat-mesh.d.ts +737 -0
  107. package/dist/splat-modifier-stack.d.ts +38 -0
  108. package/dist/splat-modifier.d.ts +156 -0
  109. package/dist/splat-partition.d.ts +37 -0
  110. package/dist/splat-query.d.ts +51 -0
  111. package/dist/splat-scene.d.ts +127 -0
  112. package/dist/splat-sort-bounds.d.ts +11 -0
  113. package/dist/static-lod-splat-mesh.d.ts +53 -0
  114. package/dist/static-lod-worker-D2fyqQZO.js +474 -0
  115. package/dist/static-lod-worker-D2fyqQZO.js.map +1 -0
  116. package/dist/static-lod-worker-protocol.d.ts +37 -0
  117. package/dist/static-lod.d.ts +24 -0
  118. package/dist/storage-attribute-mirror.d.ts +103 -0
  119. package/dist/streamed-splat-mesh-utils.d.ts +146 -0
  120. package/dist/streamed-splat-mesh.d.ts +985 -0
  121. package/dist/unified-splat-renderer.d.ts +205 -0
  122. package/dist/webgpu-limits.d.ts +133 -0
  123. package/dist/work-buffer-gather.d.ts +119 -0
  124. package/dist/work-buffer-material.d.ts +42 -0
  125. package/dist/worker-sorter.d.ts +57 -0
  126. package/dist/xr-view.d.ts +92 -0
  127. package/package.json +135 -21
@@ -0,0 +1,1098 @@
1
+ var fe = Object.defineProperty;
2
+ var le = (t, e, n) => e in t ? fe(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
+ var l = (t, e, n) => le(t, typeof e != "symbol" ? e + "" : e, n);
4
+ import { h as H } from "../half-float-Cs4FDq8Q.js";
5
+ import { w as we } from "../splat-data-BZM5dhhx.js";
6
+ import { s as D, n as ge, p as be } from "../sh-pack-D5wAe5gg.js";
7
+ import * as R from "three/webgpu";
8
+ import { t as j } from "../loading-wLo8vRbA.js";
9
+ import { l as ye } from "../splat-budget-PSojLJPO.js";
10
+ function me(t) {
11
+ return t.encoding === "f32" ? 4 : t.encoding === "f16" ? 2 : 1;
12
+ }
13
+ function z(t) {
14
+ if (t.min === void 0 || t.max === void 0)
15
+ throw new Error(`RAD "${t.property}" (${t.encoding}) is missing its min/max range.`);
16
+ return { min: t.min, max: t.max };
17
+ }
18
+ function G(t) {
19
+ if (t.max === void 0)
20
+ throw new Error(`RAD "${t.property}" (${t.encoding}) is missing its max range.`);
21
+ return t.max;
22
+ }
23
+ function A(t, e, n, s, r) {
24
+ if (t.byteLength < s * n * r)
25
+ throw new Error(
26
+ `RAD "${e.property}" column holds ${t.byteLength} bytes, fewer than the ${s * n * r} its declared ${s} splats need.`
27
+ );
28
+ }
29
+ function M(t, e, n, s) {
30
+ switch (A(t, e, n, s, me(e)), e.encoding) {
31
+ case "f32":
32
+ return Q(t, n, s);
33
+ case "f16":
34
+ return Z(t, n, s);
35
+ case "f32_lebytes":
36
+ return Ce(t, n, s);
37
+ case "f16_lebytes":
38
+ return Ae(t, n, s);
39
+ case "r8": {
40
+ const { min: r, max: o } = z(e);
41
+ return ve(t, n, s, r, o);
42
+ }
43
+ case "r8_delta": {
44
+ const { min: r, max: o } = z(e);
45
+ return De(t, n, s, r, o);
46
+ }
47
+ case "s8":
48
+ return Ee(t, n, s, G(e));
49
+ case "s8_delta":
50
+ return xe(t, n, s, G(e));
51
+ case "ln_0r8": {
52
+ const { min: r, max: o } = z(e);
53
+ return $e(t, n, s, r, o);
54
+ }
55
+ case "ln_f16":
56
+ return Fe(t, n, s);
57
+ default:
58
+ throw new Error(`Unsupported RAD "${e.property}" encoding "${e.encoding}".`);
59
+ }
60
+ }
61
+ function Re(t, e, n) {
62
+ if (e.encoding === "oct88r8")
63
+ return A(t, e, 3, n, 1), _e(t, n);
64
+ (e.encoding === "f32" || e.encoding === "f16") && A(t, e, 3, n, e.encoding === "f32" ? 4 : 2);
65
+ const s = e.encoding === "f32" ? Q(t, 3, n) : e.encoding === "f16" ? Z(t, 3, n) : void 0;
66
+ if (!s) throw new Error(`Unsupported RAD orientation encoding "${e.encoding}".`);
67
+ const r = new Float32Array(n * 4);
68
+ for (let o = 0; o < n; o++) {
69
+ const i = s[o * 3 + 0], c = s[o * 3 + 1], a = s[o * 3 + 2];
70
+ r[o * 4 + 0] = i, r[o * 4 + 1] = c, r[o * 4 + 2] = a, r[o * 4 + 3] = Math.sqrt(Math.max(0, 1 - i * i - c * c - a * a));
71
+ }
72
+ return r;
73
+ }
74
+ function Se(t, e, n) {
75
+ if (e.encoding !== "u16")
76
+ throw new Error(`Unsupported RAD child_count encoding "${e.encoding}".`);
77
+ return A(t, e, 1, n, 2), ee(t, n);
78
+ }
79
+ function ke(t, e, n) {
80
+ if (e.encoding !== "u32")
81
+ throw new Error(`Unsupported RAD child_start encoding "${e.encoding}".`);
82
+ return A(t, e, 1, n, 4), te(t, n);
83
+ }
84
+ const W = new DataView(new ArrayBuffer(4));
85
+ function pe(t) {
86
+ return W.setUint32(0, t, !0), W.getFloat32(0, !0);
87
+ }
88
+ function Q(t, e, n) {
89
+ const s = new DataView(t.buffer, t.byteOffset, t.byteLength), r = new Float32Array(n * e);
90
+ for (let o = 0; o < n; o++) {
91
+ let i = o * 4;
92
+ for (let c = 0; c < e; c++)
93
+ r[o * e + c] = s.getFloat32(i, !0), i += n * 4;
94
+ }
95
+ return r;
96
+ }
97
+ function Z(t, e, n) {
98
+ const s = new DataView(t.buffer, t.byteOffset, t.byteLength), r = new Float32Array(n * e);
99
+ for (let o = 0; o < n; o++) {
100
+ let i = o * 2;
101
+ for (let c = 0; c < e; c++)
102
+ r[o * e + c] = H(s.getUint16(i, !0)), i += n * 2;
103
+ }
104
+ return r;
105
+ }
106
+ function Ce(t, e, n) {
107
+ const s = n * e, r = new Float32Array(n * e);
108
+ for (let o = 0; o < n; o++)
109
+ for (let i = 0; i < e; i++) {
110
+ const c = n * i + o, a = (t[c] | t[c + s] << 8 | t[c + s * 2] << 16 | t[c + s * 3] << 24) >>> 0;
111
+ r[o * e + i] = pe(a);
112
+ }
113
+ return r;
114
+ }
115
+ function Ae(t, e, n) {
116
+ const s = n * e, r = new Float32Array(n * e);
117
+ for (let o = 0; o < n; o++)
118
+ for (let i = 0; i < e; i++) {
119
+ const c = n * i + o, a = t[c] | t[c + s] << 8;
120
+ r[o * e + i] = H(a);
121
+ }
122
+ return r;
123
+ }
124
+ function ve(t, e, n, s, r) {
125
+ const o = new Float32Array(n * e), i = r - s;
126
+ for (let c = 0; c < n; c++) {
127
+ let a = c;
128
+ for (let h = 0; h < e; h++)
129
+ o[c * e + h] = t[a] / 255 * i + s, a += n;
130
+ }
131
+ return o;
132
+ }
133
+ function De(t, e, n, s, r) {
134
+ const o = new Float32Array(n * e), i = r - s, c = new Uint8Array(e);
135
+ for (let a = 0; a < n; a++) {
136
+ let h = a;
137
+ for (let u = 0; u < e; u++) {
138
+ const d = c[u] + t[h] & 255;
139
+ c[u] = d, o[a * e + u] = d / 255 * i + s, h += n;
140
+ }
141
+ }
142
+ return o;
143
+ }
144
+ function Ee(t, e, n, s) {
145
+ const r = new Float32Array(n * e);
146
+ for (let o = 0; o < n; o++) {
147
+ let i = o;
148
+ for (let c = 0; c < e; c++) {
149
+ const a = t[i], h = a >= 128 ? a - 256 : a;
150
+ r[o * e + c] = h / 127 * s, i += n;
151
+ }
152
+ }
153
+ return r;
154
+ }
155
+ function xe(t, e, n, s) {
156
+ const r = new Float32Array(n * e), o = new Uint8Array(e);
157
+ for (let i = 0; i < n; i++) {
158
+ let c = i;
159
+ for (let a = 0; a < e; a++) {
160
+ const h = o[a] + t[c] & 255;
161
+ o[a] = h;
162
+ const u = h >= 128 ? h - 256 : h;
163
+ r[i * e + a] = u / 127 * s, c += n;
164
+ }
165
+ }
166
+ return r;
167
+ }
168
+ function $e(t, e, n, s, r) {
169
+ const o = new Float32Array(n * e), i = (r - s) / 254;
170
+ for (let c = 0; c < n; c++) {
171
+ let a = c;
172
+ for (let h = 0; h < e; h++) {
173
+ const u = t[a];
174
+ o[c * e + h] = u === 0 ? 0 : Math.exp(s + (u - 1) * i), a += n;
175
+ }
176
+ }
177
+ return o;
178
+ }
179
+ function Fe(t, e, n) {
180
+ const s = new DataView(t.buffer, t.byteOffset, t.byteLength), r = new Float32Array(n * e);
181
+ for (let o = 0; o < n; o++) {
182
+ let i = o * 2;
183
+ for (let c = 0; c < e; c++)
184
+ r[o * e + c] = Math.exp(H(s.getUint16(i, !0))), i += n * 2;
185
+ }
186
+ return r;
187
+ }
188
+ function _e(t, e) {
189
+ const n = new Float32Array(e * 4);
190
+ for (let s = 0; s < e; s++) {
191
+ const r = s * 3, o = t[r], i = t[r + 1], c = t[r + 2];
192
+ let a = o / 255 * 2 - 1, h = i / 255 * 2 - 1;
193
+ const u = 1 - Math.abs(a) - Math.abs(h), d = Math.max(-u, 0);
194
+ a = a >= 0 ? a - d : a + d, h = h >= 0 ? h - d : h + d;
195
+ const f = Math.hypot(a, h, u) || 1, w = c / 255 * 0.5 * Math.PI, g = Math.sin(w), b = Math.cos(w);
196
+ n[s * 4 + 0] = a / f * g, n[s * 4 + 1] = h / f * g, n[s * 4 + 2] = u / f * g, n[s * 4 + 3] = b;
197
+ }
198
+ return n;
199
+ }
200
+ function ee(t, e) {
201
+ const n = new DataView(t.buffer, t.byteOffset, t.byteLength), s = new Uint16Array(e);
202
+ for (let r = 0; r < e; r++) s[r] = n.getUint16(r * 2, !0);
203
+ return s;
204
+ }
205
+ function te(t, e) {
206
+ const n = new DataView(t.buffer, t.byteOffset, t.byteLength), s = new Uint32Array(e);
207
+ for (let r = 0; r < e; r++) s[r] = n.getUint32(r * 4, !0);
208
+ return s;
209
+ }
210
+ const Me = 809779538, ne = 1128546642, U = 1 << 24;
211
+ function oe(t, e) {
212
+ if (!Number.isSafeInteger(t) || t < 0)
213
+ throw new Error(`Invalid RAD ${e} splat count: ${t}.`);
214
+ }
215
+ function Ue(t, e) {
216
+ if (oe(t, e), t > U)
217
+ throw new Error(
218
+ `RAD ${e} declares ${t} splats, above the supported maximum of ${U}.`
219
+ );
220
+ }
221
+ async function mt(t) {
222
+ var h;
223
+ const e = new Uint8Array(t);
224
+ if (e.byteLength < 8) throw new Error("Truncated RAD file.");
225
+ if (new DataView(e.buffer, e.byteOffset, e.byteLength).getUint32(0, !0) === ne)
226
+ return Y([await L(e, 0)]);
227
+ const { meta: r, chunksStart: o } = re(t);
228
+ if (r.count > U)
229
+ throw new Error(
230
+ `RAD header declares ${r.count} splats, above the supported maximum of ${U} for a one-shot decode; load this file as a streamed scene instead.`
231
+ );
232
+ const i = [];
233
+ let c, a;
234
+ for (const [u, d] of r.chunks.entries()) {
235
+ if (d.filename !== void 0)
236
+ throw new Error(
237
+ `RAD chunk ${u} is an external .radc file ("${d.filename}"); one-shot loading only supports a single self-contained .rad file.`
238
+ );
239
+ const f = o + d.offset;
240
+ if (f + d.bytes > e.byteLength)
241
+ throw new Error(`RAD chunk ${u} extends past the file.`);
242
+ const w = await L(e, f, !0, c, a);
243
+ c ?? (c = w.shCodebook), a ?? (a = (h = w.shPacked) == null ? void 0 : h.range.max[0]), i.push(w);
244
+ }
245
+ return Y(i);
246
+ }
247
+ function re(t) {
248
+ const e = new Uint8Array(t);
249
+ if (e.byteLength < 8) throw new Error("Truncated RAD file.");
250
+ const n = new DataView(e.buffer, e.byteOffset, e.byteLength), s = n.getUint32(0, !0);
251
+ if (s !== Me)
252
+ throw new Error(`Not a RAD file (magic 0x${s.toString(16).padStart(8, "0")}).`);
253
+ const r = n.getUint32(4, !0);
254
+ if (e.byteLength < 8 + r) throw new Error("Truncated RAD header.");
255
+ const o = JSON.parse(ie(e, 8, r));
256
+ if (o.version !== 1) throw new Error(`Unsupported RAD version ${o.version}.`);
257
+ if (o.type !== "gsplat") throw new Error(`Unsupported RAD type "${o.type}".`);
258
+ if (oe(o.count, "header"), !Array.isArray(o.chunks)) throw new Error("RAD header is missing its chunk list.");
259
+ const i = o.chunks;
260
+ for (const [c, a] of i.entries())
261
+ if (!Number.isSafeInteger(a == null ? void 0 : a.offset) || a.offset < 0 || !Number.isSafeInteger(a == null ? void 0 : a.bytes) || a.bytes < 0)
262
+ throw new Error(`RAD chunk ${c} declares an invalid byte range.`);
263
+ if (o.chunkSize !== void 0) {
264
+ if (!Number.isSafeInteger(o.chunkSize) || o.chunkSize <= 0 || o.chunkSize > U)
265
+ throw new Error(`Invalid RAD chunk size: ${o.chunkSize}.`);
266
+ if (o.count > o.chunkSize * i.length)
267
+ throw new Error(
268
+ `RAD header declares ${o.count} splats, more than its ${i.length} chunks of ${o.chunkSize} can hold.`
269
+ );
270
+ }
271
+ return { meta: o, chunksStart: 8 + se(r) };
272
+ }
273
+ async function Be(t, e, n, s = !0) {
274
+ const r = await L(new Uint8Array(t), 0, !0, e, n);
275
+ return Pe(r, s);
276
+ }
277
+ function Pe(t, e) {
278
+ const { count: n } = t, s = new Float32Array(n * 3), r = new Uint8Array(n * 4), o = new Float32Array(n * 6);
279
+ if (!t.childCount || !t.childStart) {
280
+ for (let w = 0; w < n; w++) O(t, w, w, s, r, o);
281
+ return {
282
+ count: n,
283
+ positions: s,
284
+ colors: r,
285
+ covariances: o,
286
+ ...t.shPacked ? { shPacked: t.shPacked } : {},
287
+ ...t.shCodebook ? { radShCodebook: t.shCodebook } : {}
288
+ };
289
+ }
290
+ const i = t.childCount, c = t.childStart, a = e ? ze(i, c, n) : Ne(n), h = Ie(t.shPacked, a), u = new Uint16Array(n), d = new Uint32Array(n), f = new Float32Array(n);
291
+ for (let w = 0; w < n; w++) {
292
+ const g = a[w];
293
+ O(t, g, w, s, r, o), u[w] = i[g], d[w] = c[g], f[w] = Te(t.alpha[g], t.scale, g);
294
+ }
295
+ return {
296
+ count: n,
297
+ positions: s,
298
+ colors: r,
299
+ covariances: o,
300
+ radTree: { childCount: u, childStart: d, size: f },
301
+ ...h ? { shPacked: h } : {},
302
+ ...t.shCodebook ? { radShCodebook: t.shCodebook } : {}
303
+ };
304
+ }
305
+ function Te(t, e, n) {
306
+ const s = (e[n * 3] + e[n * 3 + 1] + e[n * 3 + 2]) / 3;
307
+ return 2 * (Math.max(t, 1) * 4 - 3) * s;
308
+ }
309
+ function Ne(t) {
310
+ const e = new Uint32Array(t);
311
+ for (let n = 0; n < t; n++) e[n] = n;
312
+ return e;
313
+ }
314
+ function ze(t, e, n) {
315
+ const s = new Uint32Array(n);
316
+ for (let o = 0; o < n; o++) s[o] = o;
317
+ const r = (o) => t[o] === 0 ? Number.MAX_SAFE_INTEGER : e[o];
318
+ return s.sort((o, i) => r(i) - r(o));
319
+ }
320
+ async function L(t, e, n, s, r) {
321
+ const o = new DataView(t.buffer, t.byteOffset, t.byteLength), i = o.getUint32(e, !0);
322
+ if (i !== ne)
323
+ throw new Error(`Not a RAD chunk (magic 0x${i.toString(16).padStart(8, "0")}).`);
324
+ const c = o.getUint32(e + 4, !0), a = e + 8 + se(c), h = JSON.parse(ie(t, e + 8, c));
325
+ if (h.version !== 1)
326
+ throw new Error(`Unsupported RAD chunk version ${h.version}.`);
327
+ const u = Number(o.getBigUint64(a, !0)), d = a + 8;
328
+ if (d + u > t.byteLength)
329
+ throw new Error("RAD chunk payload extends past the file.");
330
+ const { count: f } = h;
331
+ Ue(f, "chunk");
332
+ const w = h.lodTree ?? !1;
333
+ if (!Array.isArray(h.properties))
334
+ throw new Error("RAD chunk metadata is missing its property list.");
335
+ const g = h.properties, b = new Map(g.map((p) => [p.property, p])), y = async (p) => {
336
+ const S = b.get(p);
337
+ if (!S) return;
338
+ if (!Number.isSafeInteger(S.offset) || S.offset < 0 || !Number.isSafeInteger(S.bytes) || S.bytes < 0)
339
+ throw new Error(`RAD property "${p}" declares an invalid byte range.`);
340
+ const N = d + S.offset;
341
+ if (S.offset + S.bytes > u || N + S.bytes > t.byteLength)
342
+ throw new Error(`RAD property "${p}" extends past its chunk.`);
343
+ const q = t.subarray(N, N + S.bytes);
344
+ return S.compression === void 0 ? q : We(q);
345
+ }, C = w, B = [
346
+ "center",
347
+ "alpha",
348
+ "rgb",
349
+ "scales",
350
+ "orientation",
351
+ ...C ? ["child_count", "child_start"] : [void 0, void 0],
352
+ "sh1",
353
+ "sh2",
354
+ "sh3",
355
+ "sh_label",
356
+ "sh1_code",
357
+ "sh2_code",
358
+ "sh3_code"
359
+ ], [
360
+ E,
361
+ x,
362
+ $,
363
+ v,
364
+ F,
365
+ _,
366
+ m,
367
+ k,
368
+ P,
369
+ ce,
370
+ ae,
371
+ he,
372
+ ue,
373
+ de
374
+ ] = await Promise.all(
375
+ B.map((p) => p === void 0 ? Promise.resolve(void 0) : y(p))
376
+ );
377
+ if (!E) throw new Error('RAD chunk is missing its "center" property.');
378
+ if (!x) throw new Error('RAD chunk is missing its "alpha" property.');
379
+ if (!$) throw new Error('RAD chunk is missing its "rgb" property.');
380
+ if (!v) throw new Error('RAD chunk is missing its "scales" property.');
381
+ if (!F) throw new Error('RAD chunk is missing its "orientation" property.');
382
+ return {
383
+ count: f,
384
+ lodTree: w,
385
+ center: M(E, b.get("center"), 3, f),
386
+ alpha: M(x, b.get("alpha"), 1, f),
387
+ rgb: M($, b.get("rgb"), 3, f),
388
+ scale: M(v, b.get("scales"), 3, f),
389
+ quat: Re(F, b.get("orientation"), f),
390
+ ...C && _ ? { childCount: Se(_, b.get("child_count"), f) } : {},
391
+ ...C && m ? { childStart: ke(m, b.get("child_start"), f) } : {},
392
+ ...Le({
393
+ count: f,
394
+ byName: b,
395
+ sh1Raw: k,
396
+ sh2Raw: P,
397
+ sh3Raw: ce,
398
+ shLabelRaw: ae,
399
+ sh1CodeRaw: he,
400
+ sh2CodeRaw: ue,
401
+ sh3CodeRaw: de,
402
+ knownShCodebook: s,
403
+ knownShExtent: r
404
+ })
405
+ };
406
+ }
407
+ function Y(t) {
408
+ var a;
409
+ let e = 0;
410
+ for (const h of t)
411
+ e += h.childCount ? Ge(h.childCount) : h.count;
412
+ if (e === 0) throw new Error("RAD file has no leaf splats.");
413
+ const n = new Float32Array(e * 3), s = new Uint8Array(e * 4), r = new Float32Array(e * 6), o = (a = t.find((h) => h.shPacked)) == null ? void 0 : a.shPacked, i = o ? new Uint32Array(e * D(o.bands)) : void 0;
414
+ i && o && i.fill(ge(o.range));
415
+ let c = 0;
416
+ for (const h of t)
417
+ for (let u = 0; u < h.count; u++)
418
+ if (!(h.childCount && h.childCount[u] !== 0)) {
419
+ if (O(h, u, c, n, s, r), i && h.shPacked) {
420
+ const d = D(o.bands);
421
+ i.set(h.shPacked.packed.subarray(u * d, (u + 1) * d), c * d);
422
+ }
423
+ c++;
424
+ }
425
+ return {
426
+ count: e,
427
+ positions: n,
428
+ colors: s,
429
+ covariances: r,
430
+ ...o && i ? { shPacked: { ...o, packed: i } } : {}
431
+ };
432
+ }
433
+ function Ie(t, e) {
434
+ if (!t) return;
435
+ const n = D(t.bands), s = new Uint32Array(t.packed.length);
436
+ for (let r = 0; r < e.length; r++) {
437
+ const o = e[r];
438
+ s.set(t.packed.subarray(o * n, (o + 1) * n), r * n);
439
+ }
440
+ return { ...t, packed: s };
441
+ }
442
+ function Le(t) {
443
+ const e = [t.sh1Raw, t.sh2Raw, t.sh3Raw], n = [t.sh1CodeRaw, t.sh2CodeRaw, t.sh3CodeRaw], s = e.reduce((h, u, d) => u ? d + 1 : h, 0), r = n.reduce((h, u, d) => u ? d + 1 : h, 0);
444
+ if (s === 0 && r === 0 && !t.shLabelRaw) return {};
445
+ if (s > 0 && r > 0)
446
+ throw new Error("RAD chunk mixes direct and codebook SH columns.");
447
+ let o, i, c, a;
448
+ if (s > 0)
449
+ i = s, o = J(e, t.byName, t.count, i), a = Oe(t.byName, i) ?? t.knownShExtent;
450
+ else {
451
+ if (c = t.knownShCodebook, !c && r > 0) {
452
+ i = r;
453
+ const d = Ve(
454
+ n[r - 1],
455
+ t.byName.get(`sh${r}_code`)
456
+ );
457
+ c = {
458
+ bands: i,
459
+ count: d,
460
+ coefficients: J(n, t.byName, d, i, "_code")
461
+ };
462
+ }
463
+ if (!c) throw new Error("RAD SH labels require the codebook stored in chunk zero.");
464
+ if (!t.shLabelRaw)
465
+ throw new Error('RAD SH codebook chunk is missing its "sh_label" property.');
466
+ i = c.bands;
467
+ const h = qe(t.shLabelRaw, t.byName.get("sh_label"), t.count);
468
+ o = new Float32Array(t.count * D(i) * 3);
469
+ const u = D(i) * 3;
470
+ for (let d = 0; d < t.count; d++) {
471
+ const f = h[d];
472
+ if (f >= c.count)
473
+ throw new Error(`RAD SH label ${f} exceeds its ${c.count}-entry codebook.`);
474
+ o.set(
475
+ c.coefficients.subarray(f * u, (f + 1) * u),
476
+ d * u
477
+ );
478
+ }
479
+ a = 0;
480
+ for (const d of c.coefficients) a = Math.max(a, Math.abs(d));
481
+ }
482
+ return {
483
+ shPacked: be(o, t.count, i, a),
484
+ ...c ? { shCodebook: c } : {}
485
+ };
486
+ }
487
+ function Oe(t, e, n = "") {
488
+ let s = 0;
489
+ for (let r = 1; r <= e; r++) {
490
+ const o = t.get(`sh${r}${n}`);
491
+ if (!o) return;
492
+ let i;
493
+ switch (o.encoding) {
494
+ case "s8":
495
+ case "s8_delta":
496
+ if (o.max === void 0) return;
497
+ i = Math.abs(o.max);
498
+ break;
499
+ case "r8":
500
+ case "r8_delta":
501
+ if (o.min === void 0 || o.max === void 0) return;
502
+ i = Math.max(Math.abs(o.min), Math.abs(o.max));
503
+ break;
504
+ default:
505
+ return;
506
+ }
507
+ s = Math.max(s, i);
508
+ }
509
+ return s;
510
+ }
511
+ function J(t, e, n, s, r = "") {
512
+ const o = D(s), i = new Float32Array(n * o * 3);
513
+ let c = 0;
514
+ for (let a = 1; a <= s; a++) {
515
+ const h = t[a - 1], u = e.get(`sh${a}${r}`);
516
+ if (!h || !u) throw new Error(`RAD SH is missing its "sh${a}${r}" column.`);
517
+ const d = a === 1 ? 9 : a === 2 ? 15 : 21, f = M(h, u, d, n);
518
+ for (let w = 0; w < n; w++) {
519
+ const g = (w * o + c) * 3;
520
+ for (let b = 0; b < d; b++) i[g + b] = f[w * d + b];
521
+ }
522
+ c += d / 3;
523
+ }
524
+ return i;
525
+ }
526
+ function Ve(t, e) {
527
+ const n = e.property.startsWith("sh1") ? 9 : e.property.startsWith("sh2") ? 15 : 21, s = He(e);
528
+ if (t.byteLength % (n * s) !== 0)
529
+ throw new Error(`RAD "${e.property}" has an invalid SH column length.`);
530
+ return t.byteLength / (n * s);
531
+ }
532
+ function He(t) {
533
+ switch (t.encoding) {
534
+ case "f32":
535
+ case "f32_lebytes":
536
+ return 4;
537
+ case "f16":
538
+ case "f16_lebytes":
539
+ case "ln_f16":
540
+ return 2;
541
+ case "r8":
542
+ case "r8_delta":
543
+ case "s8":
544
+ case "s8_delta":
545
+ case "ln_0r8":
546
+ return 1;
547
+ default:
548
+ throw new Error(`Unsupported RAD "${t.property}" encoding "${t.encoding}".`);
549
+ }
550
+ }
551
+ function qe(t, e, n) {
552
+ if (e.encoding === "u16")
553
+ return A(t, e, 1, n, 2), Uint32Array.from(ee(t, n));
554
+ if (e.encoding === "u32")
555
+ return A(t, e, 1, n, 4), te(t, n);
556
+ throw new Error(`Unsupported RAD sh_label encoding "${e.encoding}".`);
557
+ }
558
+ function O(t, e, n, s, r, o) {
559
+ const i = t.alpha[e];
560
+ s[n * 3 + 0] = t.center[e * 3 + 0], s[n * 3 + 1] = t.center[e * 3 + 1], s[n * 3 + 2] = t.center[e * 3 + 2], r[n * 4 + 0] = T(t.rgb[e * 3 + 0] * 255), r[n * 4 + 1] = T(t.rgb[e * 3 + 1] * 255), r[n * 4 + 2] = T(t.rgb[e * 3 + 2] * 255), r[n * 4 + 3] = T(i * 0.5 * 255), we(
561
+ o,
562
+ n,
563
+ t.scale[e * 3 + 0],
564
+ t.scale[e * 3 + 1],
565
+ t.scale[e * 3 + 2],
566
+ t.quat[e * 4 + 3],
567
+ // w
568
+ t.quat[e * 4 + 0],
569
+ // x
570
+ t.quat[e * 4 + 1],
571
+ // y
572
+ t.quat[e * 4 + 2]
573
+ // z
574
+ );
575
+ }
576
+ function Ge(t) {
577
+ let e = 0;
578
+ for (const n of t) n === 0 && e++;
579
+ return e;
580
+ }
581
+ function T(t) {
582
+ return Math.max(0, Math.min(255, Math.round(t)));
583
+ }
584
+ function se(t) {
585
+ return t + 7 & -8;
586
+ }
587
+ function ie(t, e, n) {
588
+ return new TextDecoder().decode(t.subarray(e, e + n));
589
+ }
590
+ async function We(t) {
591
+ if (typeof DecompressionStream > "u")
592
+ throw new Error("This runtime does not provide decompression required for RAD files.");
593
+ const e = t[0] === 31 && t[1] === 139 ? "gzip" : t[0] === 120 ? "deflate" : "deflate-raw", n = new Uint8Array(t.byteLength);
594
+ n.set(t);
595
+ const s = new Blob([n.buffer]).stream().pipeThrough(new DecompressionStream(e));
596
+ return new Uint8Array(await new Response(s).arrayBuffer());
597
+ }
598
+ class Ye {
599
+ constructor(e) {
600
+ l(this, "chunkSize");
601
+ /** Parent-size ranges awaiting the child chunk that will consume them,
602
+ * keyed by that child chunk index. Each range is a global-index span. */
603
+ l(this, "pending", /* @__PURE__ */ new Map());
604
+ /** Chunks already resolved - a parent decoded after its child can't fix it,
605
+ * so we drop such stragglers rather than leak them. */
606
+ l(this, "resolved", /* @__PURE__ */ new Set());
607
+ this.chunkSize = e;
608
+ }
609
+ /**
610
+ * Records `chunkIndex`'s internal→child links and returns that chunk's
611
+ * per-splat parent sizes (in the chunk's own splat order). Call once per
612
+ * chunk, as it decodes.
613
+ *
614
+ * @param childCount - Per-splat child count (0 = leaf).
615
+ * @param childStart - Per-splat first-child global index.
616
+ * @param size - Per-splat LOD node size.
617
+ */
618
+ resolve(e, n, s, r) {
619
+ const o = n.length, i = e * this.chunkSize, c = new Float32Array(o).fill(1 / 0), a = this.pending.get(e);
620
+ if (a) {
621
+ for (const h of a) {
622
+ const u = Math.max(h.start - i, 0), d = Math.min(h.end - i, o);
623
+ for (let f = u; f < d; f++) c[f] = h.size;
624
+ }
625
+ this.pending.delete(e);
626
+ }
627
+ this.resolved.add(e);
628
+ for (let h = 0; h < o; h++) {
629
+ const u = n[h];
630
+ if (u === 0) continue;
631
+ const d = r[h];
632
+ let f = s[h];
633
+ const w = f + u;
634
+ for (; f < w; ) {
635
+ const g = Math.floor(f / this.chunkSize), b = (g + 1) * this.chunkSize, y = Math.min(w, b);
636
+ this.resolved.has(g) || this.push(g, f, y, d), f = y;
637
+ }
638
+ }
639
+ return c;
640
+ }
641
+ push(e, n, s, r) {
642
+ let o = this.pending.get(e);
643
+ o || (o = [], this.pending.set(e, o)), o.push({ start: n, end: s, size: r });
644
+ }
645
+ }
646
+ const K = 1e30, Je = 1.75, Ke = 16, X = 8, Xe = 4, je = 0.05;
647
+ class Qe {
648
+ constructor(e, n, s) {
649
+ l(this, "budget");
650
+ l(this, "lodBaseDistance");
651
+ l(this, "lodMultiplier");
652
+ l(this, "chunkSize");
653
+ l(this, "chunkCounts");
654
+ l(this, "numChunks");
655
+ l(this, "decoded", /* @__PURE__ */ new Set());
656
+ /** Real bounds of a decoded chunk (AABB of its splat centers). */
657
+ l(this, "bounds");
658
+ /** Region estimated from decoded parents, to rank an undecoded chunk. */
659
+ l(this, "estBounds");
660
+ /** Per chunk: the chunks its internal splats refine into (for prefetch). */
661
+ l(this, "childChunks");
662
+ /** Inverts the tree's forward links to give each splat its parent's size,
663
+ * for the frontier cut. Fed the same `own_size` measure the material derives. */
664
+ l(this, "parentSizes");
665
+ /** Splats chunk 0's coarse shell draws (the minimum-coverage floor). */
666
+ l(this, "coverageSplatCount", 1);
667
+ /**
668
+ * Whether per-splat `parent_size` is needed. Only the GPU cuts
669
+ * (`foveationMode: 'band' | 'frontier'`) read `frontierParent`; the page-table
670
+ * pager runs its own traversal in the worker and `gatherGlobals` never carries
671
+ * it. Computing it anyway costs a full covariance pass per chunk *and* leaves
672
+ * one pending-range object per internal node alive in {@link RadParentSizes}
673
+ * until its child chunk decodes - tens of millions of live objects on an
674
+ * 800-chunk capture, enough GC pressure to read as a frozen page.
675
+ */
676
+ l(this, "needsParentSizes", !0);
677
+ l(this, "scratchPoint", new R.Vector3());
678
+ this.chunkSize = e, this.chunkCounts = n, this.numChunks = n.length, this.budget = s.budget, this.lodBaseDistance = s.lodBaseDistance, this.lodMultiplier = s.lodMultiplier, this.bounds = Array.from({ length: this.numChunks }, () => null), this.estBounds = Array.from({ length: this.numChunks }, () => null), this.childChunks = Array.from({ length: this.numChunks }, () => null), this.parentSizes = new Ye(e);
679
+ }
680
+ onChunkDecoded(e, n) {
681
+ if (this.decoded.has(e)) return;
682
+ this.decoded.add(e), this.bounds[e] = et(n);
683
+ const s = n.radTree;
684
+ if (s) {
685
+ const r = /* @__PURE__ */ new Set(), o = n.positions;
686
+ for (let i = 0; i < s.childCount.length; i++) {
687
+ if (s.childCount[i] === 0) continue;
688
+ const c = Math.floor(s.childStart[i] / this.chunkSize);
689
+ r.add(c);
690
+ let a = this.estBounds[c];
691
+ a || (a = new R.Box3().makeEmpty(), this.estBounds[c] = a), a.expandByPoint(
692
+ this.scratchPoint.set(o[i * 3] ?? 0, o[i * 3 + 1] ?? 0, o[i * 3 + 2] ?? 0)
693
+ );
694
+ }
695
+ this.childChunks[e] = r, this.needsParentSizes && this.attachFrontierParent(e, n, s, Ze(n));
696
+ }
697
+ e === 0 && (this.coverageSplatCount = Math.max(1, this.chunkCounts[0]));
698
+ }
699
+ /**
700
+ * Computes each splat's `parent_size` for the frontier cut and packs it, with
701
+ * a leaf-sign bit, onto `data.frontierParent` (uploaded into `covarianceB.w`).
702
+ *
703
+ * `own_size` is derived from the covariance the same way the material does -
704
+ * `2·√(trace(Σ)/3)`, which with the merged-node-expanded scales already carries
705
+ * the expansion - so a node's own size and its children's `parent_size` are the
706
+ * identical measure, giving clean level transitions.
707
+ *
708
+ * The catch: this source streams chunks in **camera** order, not tree order, so
709
+ * a child frequently decodes before its parent and `RadParentSizes` returns
710
+ * `Infinity` (unknown). Making every such splat a root would defeat the cut -
711
+ * a root always passes the "parent too big" half, so it can never be culled and
712
+ * the drawn count runs away. Instead an unknown parent falls back to Spark's
713
+ * ~1.75× merge ratio (`parent ≈ own · 1.75`), which brackets the splat to one
714
+ * level per ray (a per-splat band). Chunk 0 - the always-resident coarse shell -
715
+ * keeps the true-root sentinel so far rays always have coverage.
716
+ */
717
+ attachFrontierParent(e, n, s, r) {
718
+ const o = n.count, i = this.parentSizes.resolve(e, s.childCount, s.childStart, r), c = new Float32Array(o);
719
+ for (let a = 0; a < o; a++) {
720
+ let h = i[a];
721
+ Number.isFinite(h) || (h = e === 0 ? K : r[a] * Je), h = Math.min(h, K), c[a] = s.childCount[a] === 0 ? -h : h;
722
+ }
723
+ n.frontierParent = c;
724
+ }
725
+ computeDesiredRuns(e, n, s) {
726
+ const r = this.buildResident(e, n), o = [];
727
+ let i = 0, c = 0;
728
+ for (const h of r)
729
+ o.push(this.wholeChunkRun(h)), i += this.chunkCounts[h], c = Math.max(c, this.chunkDistance(h, e, n));
730
+ const a = i >= this.budget ? c : 1 / 0;
731
+ for (const h of this.nearestUndecodedRefinements(r, e, n, a))
732
+ o.push(this.wholeChunkRun(h));
733
+ return o;
734
+ }
735
+ /** The coarse shell covers far/loading regions via the band cull, so there
736
+ * are no separate substitute runs. */
737
+ coarsestRunsFor() {
738
+ return [];
739
+ }
740
+ /** Chunk 0's coarse shell plus the nearest decoded chunks, up to the budget. */
741
+ buildResident(e, n) {
742
+ const s = /* @__PURE__ */ new Set();
743
+ let r = 0;
744
+ for (let i = 0; i < Math.min(Xe, this.numChunks); i++)
745
+ this.decoded.has(i) && (s.add(i), r += this.chunkCounts[i]);
746
+ const o = [...this.decoded].filter((i) => !s.has(i)).sort(
747
+ (i, c) => this.chunkDistance(i, e, n) - this.chunkDistance(c, e, n)
748
+ );
749
+ for (const i of o) {
750
+ if (r >= this.budget) break;
751
+ s.add(i), r += this.chunkCounts[i];
752
+ }
753
+ return s;
754
+ }
755
+ nearestUndecodedRefinements(e, n, s, r) {
756
+ const o = /* @__PURE__ */ new Set();
757
+ for (const i of e) {
758
+ const c = this.childChunks[i];
759
+ if (c)
760
+ for (const a of c)
761
+ !this.decoded.has(a) && this.chunkDistance(a, n, s) <= r && o.add(a);
762
+ }
763
+ return [...o].sort(
764
+ (i, c) => this.refineScore(c, n, s) - this.refineScore(i, n, s)
765
+ ).slice(0, Ke);
766
+ }
767
+ /** Refine priority for an undecoded chunk (higher = sooner): nearest, in-view
768
+ * region first, scored against its *localized* estimated bounds (not the
769
+ * scene-spanning whole-chunk bounds). Off-screen regions - not drawn - are
770
+ * strongly penalized so the surfaces the camera faces refine first. */
771
+ refineScore(e, n, s) {
772
+ const r = this.estBounds[e] ?? this.bounds[e];
773
+ if (!r) return 0;
774
+ const o = Math.max(r.distanceToPoint(n), je);
775
+ return (s.intersectsBox(r) ? 1 : 1 / X) / o;
776
+ }
777
+ chunkDistance(e, n, s) {
778
+ const r = this.bounds[e] ?? this.estBounds[e];
779
+ if (!r) return 1 / 0;
780
+ const o = r.distanceToPoint(n);
781
+ return s.intersectsBox(r) ? o : o * X;
782
+ }
783
+ /** One run covering a whole chunk; the material cull selects splats within. */
784
+ wholeChunkRun(e) {
785
+ return {
786
+ file: e,
787
+ level: this.numChunks - e,
788
+ offset: 0,
789
+ count: this.chunkCounts[e],
790
+ leafStart: e,
791
+ leafEnd: e + 1
792
+ };
793
+ }
794
+ }
795
+ function Ze(t) {
796
+ const e = t.count, n = t.covariances, s = new Float32Array(e);
797
+ for (let r = 0; r < e; r++) {
798
+ const o = n[r * 6] + n[r * 6 + 3] + n[r * 6 + 5];
799
+ s[r] = 2 * Math.sqrt(Math.max(o, 0) / 3);
800
+ }
801
+ return s;
802
+ }
803
+ function et(t) {
804
+ const e = new R.Box3(), n = new R.Vector3(), s = t.positions;
805
+ for (let r = 0; r < t.count; r++)
806
+ n.set(s[r * 3] ?? 0, s[r * 3 + 1] ?? 0, s[r * 3 + 2] ?? 0), e.expandByPoint(n);
807
+ return e.isEmpty() ? new R.Box3(new R.Vector3(-1, -1, -1), new R.Vector3(1, 1, 1)) : e;
808
+ }
809
+ const tt = 6;
810
+ async function Rt(t, e, n, s = 3, r = !0) {
811
+ var F, _;
812
+ const { meta: o, chunksStart: i } = await ut(t.manifestUrl, n), c = o.chunkSize ?? o.count;
813
+ if (o.chunks.length === 0) throw new Error("RAD file has no chunks.");
814
+ const h = o.chunks.map((m, k) => {
815
+ if (m.filename === void 0) return `${t.manifestUrl}#rad-chunk-${k}`;
816
+ const P = t.resolve(m.filename);
817
+ if (P === null)
818
+ throw new Error(`RAD chunk ${k} references a missing file "${m.filename}".`);
819
+ return P;
820
+ });
821
+ let u = o.chunks.map((m) => ({
822
+ format: "rad-chunk",
823
+ rad: m.filename === void 0 ? { start: i + m.offset, length: m.bytes } : {}
824
+ }));
825
+ const d = rt(o.comment) ?? o.count, f = r ? ye(e.budget, d) : e.budget, w = d > nt || d > f, g = o.chunks[0], b = g.filename === void 0 ? await V(
826
+ t.manifestUrl,
827
+ i + g.offset,
828
+ g.bytes,
829
+ n
830
+ ) : await dt(h[0], n), y = await Be(b, void 0, void 0, !w), C = (F = y.shPacked) == null ? void 0 : F.range.max[0];
831
+ (y.radShCodebook || C !== void 0) && (u = u.map((m) => ({
832
+ ...m,
833
+ rad: {
834
+ ...m.rad,
835
+ ...y.radShCodebook ? { shCodebook: y.radShCodebook } : {},
836
+ ...C !== void 0 ? { shExtent: C } : {}
837
+ }
838
+ })));
839
+ const B = ct(y), E = it(y.positions, y.count), x = s === 0 ? 0 : ((_ = y.shPacked) == null ? void 0 : _.bands) ?? 0, $ = o.chunks.map(
840
+ (m, k) => Math.min(c, o.count - k * c)
841
+ );
842
+ if (w) {
843
+ const m = new Qe(c, $, e);
844
+ return y.radTree && m.onChunkDecoded(0, y), {
845
+ source: m,
846
+ chunkUrls: h,
847
+ chunkKind: "file",
848
+ // File order (no leaves-first reorder): the band cull is order-agnostic
849
+ // and this keeps `child_start` valid for future exact-parent-size work.
850
+ chunkOptions: u.map((k) => ({
851
+ ...k,
852
+ rad: { ...k.rad, reorder: !1 }
853
+ })),
854
+ shBands: x,
855
+ bounds: B,
856
+ pinnedFiles: /* @__PURE__ */ new Set([0]),
857
+ // Pool holds the resident set (near + coarse), not the whole tree.
858
+ maxResidentSplats: e.budget,
859
+ // The real capture size, which `maxResidentSplats` cannot carry here (it
860
+ // echoes the caller's budget). A host splitting one budget across several
861
+ // streamed meshes needs it: a mesh cannot spend more than it contains.
862
+ contentSplatCount: d,
863
+ minimumCoverageSplats: m.coverageSplatCount,
864
+ foveation: ot,
865
+ chunkSize: c,
866
+ overviewPositions: E,
867
+ // Hand chunk 0 straight to the page-table worker: it holds the tree roots,
868
+ // so until it lands every traversal returns an empty frontier. Its buffers
869
+ // are transferred there, so nothing may read `chunk0Data` after this.
870
+ bootstrapChunk: { file: 0, data: y }
871
+ };
872
+ }
873
+ const v = new at(o.count, c, $, e);
874
+ return y.radTree && v.onChunkDecoded(0, y), {
875
+ source: v,
876
+ chunkUrls: h,
877
+ chunkKind: "file",
878
+ chunkOptions: u,
879
+ shBands: x,
880
+ bounds: B,
881
+ pinnedFiles: /* @__PURE__ */ new Set([0]),
882
+ maxResidentSplats: d,
883
+ contentSplatCount: d,
884
+ minimumCoverageSplats: v.coverageSplatCount,
885
+ overviewPositions: E
886
+ };
887
+ }
888
+ const nt = 6e6, ot = { minScreenRadiusPx: 1.6, maxScreenRadiusPx: 4 };
889
+ function rt(t) {
890
+ if (t)
891
+ try {
892
+ const n = JSON.parse(t).input_splat_count;
893
+ return typeof n == "number" && Number.isFinite(n) && n > 0 ? n : void 0;
894
+ } catch {
895
+ return;
896
+ }
897
+ }
898
+ const st = 8192;
899
+ function it(t, e) {
900
+ if (e <= 0) return new Float32Array(0);
901
+ const n = Math.max(1, Math.floor(e / st));
902
+ let s = 0;
903
+ for (let i = 0; i < e; i += n) s++;
904
+ const r = new Float32Array(s * 3);
905
+ let o = 0;
906
+ for (let i = 0; i < e; i += n) {
907
+ const c = i * 3;
908
+ r[o++] = t[c], r[o++] = t[c + 1], r[o++] = t[c + 2];
909
+ }
910
+ return r;
911
+ }
912
+ function ct(t) {
913
+ const e = new R.Box3(), n = new R.Vector3(), s = t.positions;
914
+ for (let r = 0; r < t.count; r++)
915
+ n.set(s[r * 3] ?? 0, s[r * 3 + 1] ?? 0, s[r * 3 + 2] ?? 0), e.expandByPoint(n);
916
+ return e.isEmpty() ? new R.Box3(new R.Vector3(-1, -1, -1), new R.Vector3(1, 1, 1)) : e;
917
+ }
918
+ class at {
919
+ constructor(e, n, s, r) {
920
+ l(this, "budget");
921
+ l(this, "lodBaseDistance");
922
+ l(this, "lodMultiplier");
923
+ l(this, "total");
924
+ l(this, "chunkSize");
925
+ l(this, "chunkCounts");
926
+ l(this, "numChunks");
927
+ /**
928
+ * Decoded per-splat tree columns, by chunk index.
929
+ *
930
+ * Grows monotonically and is deliberately never evicted. That costs ~10 B per
931
+ * splat of every chunk ever decoded (`childCount` u16 + `childStart` u32 +
932
+ * `size` f32) and is invisible to the chunk cache's byte accounting, so it
933
+ * reads like a leak - but it is not reclaimable. {@link discoveredDepth} is
934
+ * the longest *contiguous* decoded prefix and every frontier walk at or below
935
+ * it reads these columns, so dropping an entry would either corrupt the
936
+ * traversal or force `discoveredDepth` back and visibly coarsen the scene.
937
+ *
938
+ * Bounded in practice by the mode that uses it: `.rad` defaults to
939
+ * `foveationMode: 'pagetable'`, where the frontier worker owns the traversal
940
+ * and this source is never constructed. Only the `'band'` and `'frontier'`
941
+ * A/B modes pay it. Reducing it means changing the tree encoding, not adding
942
+ * an eviction pass here.
943
+ */
944
+ l(this, "decoded", /* @__PURE__ */ new Map());
945
+ /** Longest contiguous decoded prefix `{0 … discoveredDepth − 1}`. */
946
+ l(this, "discoveredDepth", 0);
947
+ /** Chunk-0-only frontier: the always-available coarse substitute coverage. */
948
+ l(this, "coarseRuns", []);
949
+ /** Splats the coarse (chunk-0) coverage draws. */
950
+ l(this, "coverageSplatCount", 1);
951
+ /** Cache of the last emitted frontier, keyed by the inputs that shape it. */
952
+ l(this, "cachedRuns", []);
953
+ l(this, "cachedFrontierCount", 0);
954
+ /** Frontier splat count by prefix depth; see {@link countAtDepth}. */
955
+ l(this, "depthCounts", /* @__PURE__ */ new Map());
956
+ /** Budget range over which {@link cachedRuns} stays the correct frontier. */
957
+ l(this, "cachedBudgetLo", Number.POSITIVE_INFINITY);
958
+ l(this, "cachedBudgetHi", Number.NEGATIVE_INFINITY);
959
+ l(this, "cachedForDiscovered", -1);
960
+ this.total = e, this.chunkSize = n, this.chunkCounts = s, this.numChunks = s.length, this.budget = r.budget, this.lodBaseDistance = r.lodBaseDistance, this.lodMultiplier = r.lodMultiplier;
961
+ }
962
+ onChunkDecoded(e, n) {
963
+ if (!(!n.radTree || this.decoded.has(e)))
964
+ for (this.decoded.set(e, n.radTree), e === 0 && (this.coarseRuns = this.frontierRuns(1, this.chunkCounts[0]), this.coverageSplatCount = Math.max(1, ht(this.coarseRuns))); this.decoded.has(this.discoveredDepth); ) this.discoveredDepth++;
965
+ }
966
+ computeDesiredRuns(e, n, s) {
967
+ if (this.ensureFrontier(), this.cachedFrontierCount >= this.budget) return this.cachedRuns;
968
+ const r = [...this.cachedRuns], o = Math.min(this.discoveredDepth + tt, this.numChunks);
969
+ for (let i = this.discoveredDepth; i < o; i++)
970
+ this.decoded.has(i) || r.push(this.fetchIntentRun(i));
971
+ return r;
972
+ }
973
+ coarsestRunsFor(e, n) {
974
+ const s = [];
975
+ for (const r of this.coarseRuns)
976
+ r.leafStart < n && r.leafEnd > e && s.push(r);
977
+ return s;
978
+ }
979
+ /**
980
+ * Recomputes the cached frontier when the budget or discovery has moved.
981
+ *
982
+ * The cache is keyed on the *range* of budgets that select the same prefix
983
+ * depth, not on the budget itself. A camera-weighted governor rewrites the
984
+ * budget every frame - it wanders by a few percent as the camera drifts - and
985
+ * an exact-equality key therefore missed on roughly a third of all calls while
986
+ * choosing the very same depth. Each of those misses re-walks `frontierRuns`
987
+ * over the whole decoded prefix (millions of splats, several times over if it
988
+ * steps down levels): measured at 33ms mean and 1.1s worst, it was 92% of all
989
+ * frame CPU on a marker-heavy scene. Frontier count rises monotonically with
990
+ * depth, so the selected depth is stable for any budget in
991
+ * `[thisDepthCount, nextDepthCount)` and the walk can be skipped outright.
992
+ */
993
+ ensureFrontier() {
994
+ if (this.cachedForDiscovered === this.discoveredDepth && this.budget >= this.cachedBudgetLo && this.budget < this.cachedBudgetHi)
995
+ return;
996
+ this.cachedForDiscovered = this.discoveredDepth;
997
+ let e = this.discoveredDepth, n = e > 0 ? this.countAtDepth(e) : 0, s = Number.POSITIVE_INFINITY;
998
+ for (; e > 1 && n > this.budget; )
999
+ s = n, e--, n = this.countAtDepth(e);
1000
+ const r = e > 0 ? this.frontierRuns(e, Math.min(e * this.chunkSize, this.total)) : [];
1001
+ this.cachedRuns = r, this.cachedFrontierCount = n, this.cachedBudgetLo = e > 1 ? n : 0, this.cachedBudgetHi = s;
1002
+ }
1003
+ /**
1004
+ * Splats the frontier draws at `depth`, memoized.
1005
+ *
1006
+ * `discoveredDepth` is the longest *contiguous* decoded prefix, so for any
1007
+ * `depth <= discoveredDepth` every chunk the walk reads is already decoded and
1008
+ * the answer can never change afterwards - chunks are only ever added to
1009
+ * `decoded`. That makes this permanently cacheable, which is what lets the
1010
+ * depth search above run without rebuilding runs at each level it rejects.
1011
+ */
1012
+ countAtDepth(e) {
1013
+ const n = this.depthCounts.get(e);
1014
+ if (n !== void 0) return n;
1015
+ const s = Math.min(e * this.chunkSize, this.total);
1016
+ let r = 0;
1017
+ for (let o = 0; o < e; o++) {
1018
+ const i = this.decoded.get(o);
1019
+ if (!i) continue;
1020
+ const c = this.chunkCounts[o];
1021
+ for (let a = 0; a < c; a++)
1022
+ (i.childCount[a] === 0 || i.childStart[a] >= s) && r++;
1023
+ }
1024
+ return this.depthCounts.set(e, r), r;
1025
+ }
1026
+ /**
1027
+ * The rendered runs for a resident prefix of `depth` chunks: within each
1028
+ * chunk, the contiguous ranges of splats that are leaves or whose children
1029
+ * fall beyond `residentCount` (the prefix's total splat count). The chunk's
1030
+ * splats are child-start-descending (leaves first), so each range coalesces
1031
+ * into one run.
1032
+ */
1033
+ frontierRuns(e, n) {
1034
+ const s = [];
1035
+ for (let r = 0; r < e; r++) {
1036
+ const o = this.decoded.get(r);
1037
+ if (!o) continue;
1038
+ const i = this.chunkCounts[r], c = r * this.chunkSize, a = this.numChunks - r;
1039
+ let h = -1;
1040
+ for (let u = 0; u < i; u++)
1041
+ o.childCount[u] === 0 || o.childStart[u] >= n ? h < 0 && (h = u) : h >= 0 && (s.push(I(r, a, h, u, c)), h = -1);
1042
+ h >= 0 && s.push(I(r, a, h, i, c));
1043
+ }
1044
+ return s;
1045
+ }
1046
+ fetchIntentRun(e) {
1047
+ const n = this.chunkCounts[e];
1048
+ return I(e, this.numChunks - e, 0, n, e * this.chunkSize);
1049
+ }
1050
+ }
1051
+ function I(t, e, n, s, r) {
1052
+ return { file: t, level: e, offset: n, count: s - n, leafStart: r + n, leafEnd: r + s };
1053
+ }
1054
+ function ht(t) {
1055
+ let e = 0;
1056
+ for (const n of t) e += n.count;
1057
+ return e;
1058
+ }
1059
+ async function ut(t, e) {
1060
+ let s = await V(t, 0, 262144, e, !0);
1061
+ const r = new DataView(s);
1062
+ if (r.byteLength >= 8) {
1063
+ const o = r.getUint32(4, !0);
1064
+ 8 + o > s.byteLength && (s = await V(t, 0, 8 + o, e));
1065
+ }
1066
+ return re(s);
1067
+ }
1068
+ async function V(t, e, n, s, r = !1) {
1069
+ const o = j(s), i = await fetch(t, {
1070
+ ...o,
1071
+ headers: { ...(s == null ? void 0 : s.headers) ?? {}, Range: `bytes=${e}-${e + n - 1}` }
1072
+ });
1073
+ if (!i.ok)
1074
+ throw new Error(`Failed to load ${t}: HTTP ${i.status}`);
1075
+ if (i.status !== 206 && !(r && i.status === 200))
1076
+ throw new Error(
1077
+ `${t} ignored a Range request (HTTP ${i.status}); single-file .rad streaming needs a server that answers 206 Partial Content.`
1078
+ );
1079
+ const c = await i.arrayBuffer();
1080
+ if (!r && c.byteLength !== n)
1081
+ throw new Error(`${t} returned ${c.byteLength} bytes for a ${n}-byte range.`);
1082
+ return c;
1083
+ }
1084
+ async function dt(t, e) {
1085
+ const n = await fetch(t, j(e));
1086
+ if (!n.ok) throw new Error(`Failed to load ${t}: HTTP ${n.status}`);
1087
+ return n.arrayBuffer();
1088
+ }
1089
+ export {
1090
+ ne as RAD_CHUNK_MAGIC,
1091
+ Me as RAD_MAGIC,
1092
+ at as RadLodSource,
1093
+ Rt as buildRadScene,
1094
+ mt as parseRad,
1095
+ Be as parseRadChunkStreaming,
1096
+ re as parseRadHeaderMeta
1097
+ };
1098
+ //# sourceMappingURL=rad.js.map