@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.
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/THIRD_PARTY_NOTICES.md +86 -0
- package/dist/budget-governor.d.ts +154 -0
- package/dist/camera-budget-governor.d.ts +234 -0
- package/dist/chunk-cache-budget.d.ts +156 -0
- package/dist/chunk-fetch-scheduler.d.ts +168 -0
- package/dist/chunk-loader.d.ts +85 -0
- package/dist/compute-sorter.d.ts +131 -0
- package/dist/create-splat-renderer.d.ts +99 -0
- package/dist/dataset-source.d.ts +55 -0
- package/dist/depth-of-field.d.ts +73 -0
- package/dist/effects.d.ts +337 -0
- package/dist/effects.js +180 -0
- package/dist/effects.js.map +1 -0
- package/dist/formats/ksplat/index.d.ts +8 -0
- package/dist/formats/ksplat/parse-ksplat.d.ts +9 -0
- package/dist/formats/ksplat.js +167 -0
- package/dist/formats/ksplat.js.map +1 -0
- package/dist/formats/lcc/collision-mesh.d.ts +37 -0
- package/dist/formats/lcc/collision-partition.d.ts +35 -0
- package/dist/formats/lcc/index.d.ts +16 -0
- package/dist/formats/lcc/lcc.d.ts +28 -0
- package/dist/formats/lcc/lcc2-transform.d.ts +18 -0
- package/dist/formats/lcc/lcc2.d.ts +4 -0
- package/dist/formats/lcc/parse-collision-lci.d.ts +28 -0
- package/dist/formats/lcc/parse-lcc.d.ts +177 -0
- package/dist/formats/lcc/parse-mesh-ply.d.ts +21 -0
- package/dist/formats/lcc.js +969 -0
- package/dist/formats/lcc.js.map +1 -0
- package/dist/formats/ply/index.d.ts +12 -0
- package/dist/formats/ply/parse-compressed-ply.d.ts +16 -0
- package/dist/formats/ply/parse-splat-ply.d.ts +46 -0
- package/dist/formats/ply.js +230 -0
- package/dist/formats/ply.js.map +1 -0
- package/dist/formats/rad/frontier-worker-protocol.d.ts +177 -0
- package/dist/formats/rad/index.d.ts +10 -0
- package/dist/formats/rad/parse-rad.d.ts +88 -0
- package/dist/formats/rad/rad-column-decoders.d.ts +15 -0
- package/dist/formats/rad/rad-foveated-source.d.ts +71 -0
- package/dist/formats/rad/rad-parent-sizes.d.ts +45 -0
- package/dist/formats/rad/rad.d.ts +132 -0
- package/dist/formats/rad.js +1098 -0
- package/dist/formats/rad.js.map +1 -0
- package/dist/formats/sog/index.d.ts +9 -0
- package/dist/formats/sog/parse-sog.d.ts +45 -0
- package/dist/formats/sog.js +257 -0
- package/dist/formats/sog.js.map +1 -0
- package/dist/formats/splat/index.d.ts +8 -0
- package/dist/formats/splat/parse-splat.d.ts +10 -0
- package/dist/formats/splat.js +39 -0
- package/dist/formats/splat.js.map +1 -0
- package/dist/formats/spz/index.d.ts +8 -0
- package/dist/formats/spz/parse-spz.d.ts +10 -0
- package/dist/formats/spz.js +218 -0
- package/dist/formats/spz.js.map +1 -0
- package/dist/frontier-worker-CAnK_Cmu.js +566 -0
- package/dist/frontier-worker-CAnK_Cmu.js.map +1 -0
- package/dist/half-float-Cs4FDq8Q.js +30 -0
- package/dist/half-float-Cs4FDq8Q.js.map +1 -0
- package/dist/half-float.d.ts +15 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +9054 -0
- package/dist/index.js.map +1 -0
- package/dist/load-scene.d.ts +50 -0
- package/dist/load-worker-protocol.d.ts +82 -0
- package/dist/loading-wLo8vRbA.js +102 -0
- package/dist/loading-wLo8vRbA.js.map +1 -0
- package/dist/loading.d.ts +150 -0
- package/dist/lod-manifest.d.ts +61 -0
- package/dist/lod-scheduler.d.ts +229 -0
- package/dist/lod-source.d.ts +207 -0
- package/dist/logging-BfPdd7NJ.js +19 -0
- package/dist/logging-BfPdd7NJ.js.map +1 -0
- package/dist/logging.d.ts +37 -0
- package/dist/one-shot-worker-G80btnth.js +664 -0
- package/dist/one-shot-worker-G80btnth.js.map +1 -0
- package/dist/orientation.d.ts +35 -0
- package/dist/ply-header-BwgExCn-.js +125 -0
- package/dist/ply-header-BwgExCn-.js.map +1 -0
- package/dist/ply-header.d.ts +87 -0
- package/dist/radix-sort.d.ts +28 -0
- package/dist/radix-sorter.d.ts +46 -0
- package/dist/relighting.d.ts +50 -0
- package/dist/selection-volume.d.ts +87 -0
- package/dist/sh-pack-D5wAe5gg.js +54 -0
- package/dist/sh-pack-D5wAe5gg.js.map +1 -0
- package/dist/sh-pack.d.ts +49 -0
- package/dist/sort-scheduler.d.ts +62 -0
- package/dist/sort-worker-protocol.d.ts +37 -0
- package/dist/sorter.d.ts +31 -0
- package/dist/source-transform.d.ts +57 -0
- package/dist/splat-budget-PSojLJPO.js +157 -0
- package/dist/splat-budget-PSojLJPO.js.map +1 -0
- package/dist/splat-budget.d.ts +404 -0
- package/dist/splat-data-BZM5dhhx.js +10 -0
- package/dist/splat-data-BZM5dhhx.js.map +1 -0
- package/dist/splat-data.d.ts +150 -0
- package/dist/splat-depth-pack.d.ts +39 -0
- package/dist/splat-mesh-material.d.ts +255 -0
- package/dist/splat-mesh-picking.d.ts +87 -0
- package/dist/splat-mesh-pool.d.ts +241 -0
- package/dist/splat-mesh-types-8QDjTbZO.js +390 -0
- package/dist/splat-mesh-types-8QDjTbZO.js.map +1 -0
- package/dist/splat-mesh-types.d.ts +390 -0
- package/dist/splat-mesh.d.ts +737 -0
- package/dist/splat-modifier-stack.d.ts +38 -0
- package/dist/splat-modifier.d.ts +156 -0
- package/dist/splat-partition.d.ts +37 -0
- package/dist/splat-query.d.ts +51 -0
- package/dist/splat-scene.d.ts +127 -0
- package/dist/splat-sort-bounds.d.ts +11 -0
- package/dist/static-lod-splat-mesh.d.ts +53 -0
- package/dist/static-lod-worker-D2fyqQZO.js +474 -0
- package/dist/static-lod-worker-D2fyqQZO.js.map +1 -0
- package/dist/static-lod-worker-protocol.d.ts +37 -0
- package/dist/static-lod.d.ts +24 -0
- package/dist/storage-attribute-mirror.d.ts +103 -0
- package/dist/streamed-splat-mesh-utils.d.ts +146 -0
- package/dist/streamed-splat-mesh.d.ts +985 -0
- package/dist/unified-splat-renderer.d.ts +205 -0
- package/dist/webgpu-limits.d.ts +133 -0
- package/dist/work-buffer-gather.d.ts +119 -0
- package/dist/work-buffer-material.d.ts +42 -0
- package/dist/worker-sorter.d.ts +57 -0
- package/dist/xr-view.d.ts +92 -0
- package/package.json +135 -21
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
var dt = Object.defineProperty;
|
|
2
|
+
var wt = (e, t, n) => t in e ? dt(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
|
|
3
|
+
var A = (e, t, n) => wt(e, typeof t != "symbol" ? t + "" : t, n);
|
|
4
|
+
import { w as gt } from "../splat-data-BZM5dhhx.js";
|
|
5
|
+
import { a as mt, r as pt } from "../sh-pack-D5wAe5gg.js";
|
|
6
|
+
import * as E from "three/webgpu";
|
|
7
|
+
import { M as Ct, L as yt } from "../splat-mesh-types-8QDjTbZO.js";
|
|
8
|
+
import { t as ct, a as Q } from "../loading-wLo8vRbA.js";
|
|
9
|
+
import { w as D } from "../logging-BfPdd7NJ.js";
|
|
10
|
+
import { p as vt, a as xt, b as bt, c as k, d as W } from "../ply-header-BwgExCn-.js";
|
|
11
|
+
const U = 32, O = 64, j = 1 << 24;
|
|
12
|
+
function Lt(e) {
|
|
13
|
+
return e === 1 ? 3 : e === 2 ? 8 : 15;
|
|
14
|
+
}
|
|
15
|
+
function Et(e) {
|
|
16
|
+
const t = e, n = String(t.version ?? "");
|
|
17
|
+
if (!/^[2345]\./.test(n))
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Unsupported LCC manifest version: "${n}" (expected 2.x, 3.x, 4.x or 5.x).`
|
|
20
|
+
);
|
|
21
|
+
const o = t.encoding;
|
|
22
|
+
if (o !== void 0 && o !== "COMPRESS")
|
|
23
|
+
throw new Error(`Unsupported LCC encoding: "${String(o)}" (expected COMPRESS).`);
|
|
24
|
+
const s = t.totalLevel;
|
|
25
|
+
if (!Number.isSafeInteger(s) || s < 1)
|
|
26
|
+
throw new Error(`LCC manifest declares an invalid totalLevel: ${String(s)}.`);
|
|
27
|
+
const i = s, r = t.indexDataSize, c = 4 + i * 16;
|
|
28
|
+
if (r !== c)
|
|
29
|
+
throw new Error(
|
|
30
|
+
`LCC manifest indexDataSize is ${String(r)}, but ${i} levels imply ${c}.`
|
|
31
|
+
);
|
|
32
|
+
const u = t.splats;
|
|
33
|
+
if (!Array.isArray(u) || u.length !== i)
|
|
34
|
+
throw new Error(`LCC manifest "splats" must list one count per level (${i}).`);
|
|
35
|
+
for (const w of u)
|
|
36
|
+
if (!Number.isSafeInteger(w) || w < 0)
|
|
37
|
+
throw new Error(`LCC manifest "splats" contains an invalid count: ${String(w)}.`);
|
|
38
|
+
const l = t.totalSplats;
|
|
39
|
+
if (!Number.isSafeInteger(l) || l < 0)
|
|
40
|
+
throw new Error(`LCC manifest declares an invalid totalSplats: ${String(l)}.`);
|
|
41
|
+
const a = Z(t, "cellLengthX"), f = Z(t, "cellLengthY");
|
|
42
|
+
if (a <= 0 || f <= 0)
|
|
43
|
+
throw new Error("LCC manifest cell lengths must be positive.");
|
|
44
|
+
const h = St(t), d = t.shift;
|
|
45
|
+
if (Array.isArray(d) && d.some((w) => w !== 0))
|
|
46
|
+
throw new Error(
|
|
47
|
+
`LCC manifest "shift" is non-zero (${JSON.stringify(d)}), which is not supported.`
|
|
48
|
+
);
|
|
49
|
+
const m = t.scale;
|
|
50
|
+
if (Array.isArray(m) && m.some((w) => w !== 1))
|
|
51
|
+
throw new Error(
|
|
52
|
+
`LCC manifest "scale" is non-identity (${JSON.stringify(m)}), which is not supported.`
|
|
53
|
+
);
|
|
54
|
+
const p = t.boundingBox, $ = N(p == null ? void 0 : p.min, "boundingBox.min"), b = N(p == null ? void 0 : p.max, "boundingBox.max"), g = t.attributes;
|
|
55
|
+
if (!Array.isArray(g)) throw new Error('LCC manifest is missing "attributes".');
|
|
56
|
+
const C = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const w of g)
|
|
58
|
+
typeof (w == null ? void 0 : w.name) == "string" && (!Array.isArray(w.min) || w.min.length < 3 || C.set(w.name, {
|
|
59
|
+
min: N(w.min, `attributes.${w.name}.min`),
|
|
60
|
+
max: N(w.max, `attributes.${w.name}.max`)
|
|
61
|
+
}));
|
|
62
|
+
const x = (w) => {
|
|
63
|
+
const v = C.get(w);
|
|
64
|
+
if (!v) throw new Error(`LCC manifest is missing the "${w}" attribute range.`);
|
|
65
|
+
return v;
|
|
66
|
+
}, y = C.get("shcoef") ?? Y, L = $t(t.fileType, y);
|
|
67
|
+
return {
|
|
68
|
+
totalSplats: l,
|
|
69
|
+
totalLevel: i,
|
|
70
|
+
cellLengthX: a,
|
|
71
|
+
cellLengthY: f,
|
|
72
|
+
indexDataSize: c,
|
|
73
|
+
splats: u,
|
|
74
|
+
bounds: { min: $, max: b },
|
|
75
|
+
fileType: L,
|
|
76
|
+
scale: x("scale"),
|
|
77
|
+
// A Portable capture has no SH at all; keep a benign range so the type
|
|
78
|
+
// stays total rather than sprinkling optionality through the loader.
|
|
79
|
+
shcoef: y,
|
|
80
|
+
envScale: C.get("envscale") ?? Y,
|
|
81
|
+
envShcoef: C.get("envshcoef") ?? Y,
|
|
82
|
+
georeference: h
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function St(e) {
|
|
86
|
+
const t = e.offset;
|
|
87
|
+
if (!Array.isArray(t) || !t.some((o) => o !== 0)) return;
|
|
88
|
+
const n = e.epsg;
|
|
89
|
+
return {
|
|
90
|
+
origin: N(t, "offset"),
|
|
91
|
+
epsg: Number.isSafeInteger(n) && n > 0 ? n : void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function $t(e, t) {
|
|
95
|
+
if (e === "Portable" || e === "Quality") return e;
|
|
96
|
+
if (e === void 0) return At(t) ? "Portable" : "Quality";
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Unsupported LCC fileType: "${String(e)}" (expected Portable or Quality).`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
function At(e) {
|
|
102
|
+
return e.min.every((t) => t === 0) && e.max.every((t) => t === 0 || t === 1);
|
|
103
|
+
}
|
|
104
|
+
const Y = { min: [0, 0, 0], max: [0, 0, 0] };
|
|
105
|
+
function Mt(e, t) {
|
|
106
|
+
const { indexDataSize: n, totalLevel: o } = t;
|
|
107
|
+
if (e.byteLength === 0 || e.byteLength % n !== 0)
|
|
108
|
+
throw new Error(
|
|
109
|
+
`LCC index.bin is ${e.byteLength} bytes, not a multiple of indexDataSize ${n}.`
|
|
110
|
+
);
|
|
111
|
+
const s = new DataView(e), i = [];
|
|
112
|
+
for (let r = 0; r < e.byteLength; r += n) {
|
|
113
|
+
const c = [];
|
|
114
|
+
for (let u = 0; u < o; u++) {
|
|
115
|
+
const l = r + 4 + u * 16, a = s.getUint32(l, !0), f = s.getBigUint64(l + 4, !0);
|
|
116
|
+
if (f > BigInt(Number.MAX_SAFE_INTEGER))
|
|
117
|
+
throw new Error(
|
|
118
|
+
`LCC index.bin has a byte offset beyond Number.MAX_SAFE_INTEGER: ${f}.`
|
|
119
|
+
);
|
|
120
|
+
const h = s.getUint32(l + 12, !0);
|
|
121
|
+
if (h !== a * U)
|
|
122
|
+
throw new Error(
|
|
123
|
+
`LCC index.bin level ${u} declares ${a} splats but ${h} bytes (expected ${a * U}).`
|
|
124
|
+
);
|
|
125
|
+
c.push({ count: a, byteOffset: Number(f), byteSize: h });
|
|
126
|
+
}
|
|
127
|
+
i.push({
|
|
128
|
+
cellX: s.getUint16(r, !0),
|
|
129
|
+
cellY: s.getUint16(r + 2, !0),
|
|
130
|
+
levels: c
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return i;
|
|
134
|
+
}
|
|
135
|
+
function Ut(e) {
|
|
136
|
+
const t = e >>> 30 & 3, n = [0, 0, 0, 0];
|
|
137
|
+
let o = 0;
|
|
138
|
+
for (let i = 0; i < 4; i++) {
|
|
139
|
+
if (i === t) continue;
|
|
140
|
+
const r = e >>> 10 * o & 1023;
|
|
141
|
+
n[i] = (r - 511) * Tt, o++;
|
|
142
|
+
}
|
|
143
|
+
const s = n[0] ** 2 + n[1] ** 2 + n[2] ** 2 + n[3] ** 2;
|
|
144
|
+
return n[t] = Math.sqrt(Math.max(0, 1 - s)), [n[3], n[0], n[1], n[2]];
|
|
145
|
+
}
|
|
146
|
+
const Tt = 1 / (511 * Math.SQRT2);
|
|
147
|
+
async function re(e, t, n, o) {
|
|
148
|
+
var x;
|
|
149
|
+
const s = t.stride;
|
|
150
|
+
if (s !== U && s !== U + O)
|
|
151
|
+
throw new Error(`LCC chunk declares an unsupported ${s}-byte record.`);
|
|
152
|
+
if (((x = t.sh) == null ? void 0 : x.source) === "inline" && s === U)
|
|
153
|
+
throw new Error("LCC chunk asks for inline SH but its records have no room for it.");
|
|
154
|
+
if (e.byteLength % s !== 0)
|
|
155
|
+
throw new Error(
|
|
156
|
+
`LCC chunk is ${e.byteLength} bytes, not a multiple of the ${s}-byte record.`
|
|
157
|
+
);
|
|
158
|
+
const i = e.byteLength / s;
|
|
159
|
+
if (i > j)
|
|
160
|
+
throw new Error(
|
|
161
|
+
`LCC chunk holds ${i} splats, above the supported maximum of ${j}.`
|
|
162
|
+
);
|
|
163
|
+
const r = new DataView(e), c = new Float32Array(i * 3), u = new Uint8Array(i * 4), l = new Float32Array(i * 6), a = new Uint8Array(e), { min: f, max: h } = t.scale, d = f[0], m = f[1], p = f[2], $ = h[0] - f[0], b = h[1] - f[1], g = h[2] - f[2];
|
|
164
|
+
for (let y = 0; y < i; y++) {
|
|
165
|
+
o && (y & 65535) === 0 && y > 0 && (await new Promise((S) => setTimeout(S, 0)), o.throwIfAborted());
|
|
166
|
+
const L = y * s;
|
|
167
|
+
c[y * 3 + 0] = r.getFloat32(L, !0), c[y * 3 + 1] = r.getFloat32(L + 4, !0), c[y * 3 + 2] = r.getFloat32(L + 8, !0), u[y * 4 + 0] = a[L + 12], u[y * 4 + 1] = a[L + 13], u[y * 4 + 2] = a[L + 14], u[y * 4 + 3] = a[L + 15];
|
|
168
|
+
const [w, v, F, M] = Ut(r.getUint32(L + 22, !0));
|
|
169
|
+
gt(
|
|
170
|
+
l,
|
|
171
|
+
y,
|
|
172
|
+
d + $ * (r.getUint16(L + 16, !0) / 65535),
|
|
173
|
+
m + b * (r.getUint16(L + 18, !0) / 65535),
|
|
174
|
+
p + g * (r.getUint16(L + 20, !0) / 65535),
|
|
175
|
+
w,
|
|
176
|
+
v,
|
|
177
|
+
F,
|
|
178
|
+
M
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
o == null || o.throwIfAborted();
|
|
182
|
+
const C = t.sh ? Ft(e, n, t.sh, i, s) : void 0;
|
|
183
|
+
return {
|
|
184
|
+
count: i,
|
|
185
|
+
positions: c,
|
|
186
|
+
colors: u,
|
|
187
|
+
covariances: l,
|
|
188
|
+
...C ? { shPacked: C } : {}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function Ft(e, t, n, o, s) {
|
|
192
|
+
const i = Lt(n.bands), r = new Uint32Array(o * i);
|
|
193
|
+
if (n.source === "inline") {
|
|
194
|
+
const l = new DataView(e);
|
|
195
|
+
for (let a = 0; a < o; a++) {
|
|
196
|
+
const f = a * s + U;
|
|
197
|
+
for (let h = 0; h < i; h++)
|
|
198
|
+
r[a * i + h] = l.getUint32(f + h * 4, !0);
|
|
199
|
+
}
|
|
200
|
+
return K(r, n);
|
|
201
|
+
}
|
|
202
|
+
if (!t) throw new Error("LCC chunk expected shcoef.bin bytes but none were fetched.");
|
|
203
|
+
const c = o * O;
|
|
204
|
+
if (t.byteLength !== c)
|
|
205
|
+
throw new Error(
|
|
206
|
+
`LCC shcoef.bin range is ${t.byteLength} bytes; expected ${c} for ${o} splats.`
|
|
207
|
+
);
|
|
208
|
+
const u = new DataView(t);
|
|
209
|
+
for (let l = 0; l < o; l++) {
|
|
210
|
+
const a = l * O;
|
|
211
|
+
for (let f = 0; f < i; f++)
|
|
212
|
+
r[l * i + f] = u.getUint32(a + f * 4, !0);
|
|
213
|
+
}
|
|
214
|
+
return K(r, n);
|
|
215
|
+
}
|
|
216
|
+
function K(e, t) {
|
|
217
|
+
const n = t.requantizeTo;
|
|
218
|
+
if (!n || mt(t.range, n))
|
|
219
|
+
return { bands: t.bands, packed: e, range: t.range };
|
|
220
|
+
for (let o = 0; o < e.length; o++)
|
|
221
|
+
e[o] = pt(e[o], t.range, n);
|
|
222
|
+
return { bands: t.bands, packed: e, range: n };
|
|
223
|
+
}
|
|
224
|
+
function Z(e, t) {
|
|
225
|
+
const n = e[t];
|
|
226
|
+
if (typeof n != "number" || !Number.isFinite(n))
|
|
227
|
+
throw new Error(`LCC manifest field "${t}" is not a finite number.`);
|
|
228
|
+
return n;
|
|
229
|
+
}
|
|
230
|
+
function N(e, t) {
|
|
231
|
+
if (!Array.isArray(e) || e.length < 3)
|
|
232
|
+
throw new Error(`LCC manifest "${t}" must have three numbers.`);
|
|
233
|
+
return e.slice(0, 3).map((o) => {
|
|
234
|
+
if (typeof o != "number" || !Number.isFinite(o))
|
|
235
|
+
throw new Error(`LCC manifest "${t}" has a non-finite entry.`);
|
|
236
|
+
return o;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function q() {
|
|
240
|
+
return new E.Matrix4().set(-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1);
|
|
241
|
+
}
|
|
242
|
+
function le(e) {
|
|
243
|
+
e.matrix.copy(q()), e.matrix.decompose(e.position, e.quaternion, e.scale), e.matrixWorldNeedsUpdate = !0;
|
|
244
|
+
}
|
|
245
|
+
const Bt = 128e3;
|
|
246
|
+
async function ce(e, t, n) {
|
|
247
|
+
const o = Et(e), s = (w) => {
|
|
248
|
+
const v = t.resolve(w);
|
|
249
|
+
if (v === null) throw new Error(`LCC dataset is missing "${w}".`);
|
|
250
|
+
return v;
|
|
251
|
+
}, i = s("data.bin"), r = t.resolve("environment.bin"), c = Mt(
|
|
252
|
+
await It(s("index.bin"), n, "manifest"),
|
|
253
|
+
o
|
|
254
|
+
);
|
|
255
|
+
Nt(c, o);
|
|
256
|
+
const u = o.fileType === "Quality" ? Ct : 0, l = n.shBands ?? u, a = Math.min(l, u), f = a === 0 ? void 0 : a, h = f === void 0 ? null : s("shcoef.bin"), d = [], m = [], p = (w, v) => (d.push(w), m.push(v), d.length - 1), $ = [], b = /* @__PURE__ */ new Set();
|
|
257
|
+
let g = 0;
|
|
258
|
+
const C = o.totalLevel;
|
|
259
|
+
for (let w = 0; w < c.length; w++) {
|
|
260
|
+
const v = c[w];
|
|
261
|
+
let F = 0;
|
|
262
|
+
for (let S = 0; S < C; S++) {
|
|
263
|
+
const T = v.levels[S].count;
|
|
264
|
+
if (T > 0) {
|
|
265
|
+
F = T;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const M = Math.max(1, Math.ceil(F / Bt));
|
|
270
|
+
for (let S = 0; S < M; S++) {
|
|
271
|
+
const T = new Array(C).fill(
|
|
272
|
+
void 0
|
|
273
|
+
);
|
|
274
|
+
let B = -1, H = 0;
|
|
275
|
+
for (let P = 0; P < C; P++) {
|
|
276
|
+
const _ = v.levels[P];
|
|
277
|
+
if (_.count === 0) continue;
|
|
278
|
+
const R = Math.floor(S * _.count / M), I = Math.floor((S + 1) * _.count / M);
|
|
279
|
+
if (I === R) continue;
|
|
280
|
+
const ft = `${i}#c${v.cellX}_${v.cellY}-l${P}${M > 1 ? `.${S}` : ""}`, G = p(ft, {
|
|
281
|
+
format: "lcc-bin",
|
|
282
|
+
lcc: {
|
|
283
|
+
kind: "splats",
|
|
284
|
+
start: _.byteOffset + R * U,
|
|
285
|
+
length: (I - R) * U,
|
|
286
|
+
stride: U,
|
|
287
|
+
scale: o.scale,
|
|
288
|
+
...f === void 0 ? {} : {
|
|
289
|
+
sh: {
|
|
290
|
+
bands: f,
|
|
291
|
+
range: o.shcoef,
|
|
292
|
+
source: "sidecar",
|
|
293
|
+
// shcoef.bin is aligned 2:1 with data.bin - 64 bytes per
|
|
294
|
+
// splat against 32 - so each slice's SH is its own doubled
|
|
295
|
+
// byte range, which the loader derives from `start`/`length`.
|
|
296
|
+
url: h
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
T[P] = { file: G, offset: 0, count: I - R }, B = G, H = I - R;
|
|
302
|
+
}
|
|
303
|
+
B >= 0 && (b.add(B), g += H), $.push({ bounds: Rt(v, o), lods: T, budgetGroup: w });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const x = await _t(t, o);
|
|
307
|
+
if (x > 0 && r !== null) {
|
|
308
|
+
const w = at(o), v = p(`${r}#env`, {
|
|
309
|
+
format: "lcc-bin",
|
|
310
|
+
lcc: {
|
|
311
|
+
kind: "environment",
|
|
312
|
+
start: 0,
|
|
313
|
+
length: x * w,
|
|
314
|
+
// From `fileType`, never from whether SH is enabled: a Quality
|
|
315
|
+
// environment record is 96 bytes even when its SH is being skipped.
|
|
316
|
+
stride: w,
|
|
317
|
+
scale: o.envScale,
|
|
318
|
+
...f === void 0 ? {} : {
|
|
319
|
+
sh: {
|
|
320
|
+
bands: f,
|
|
321
|
+
range: o.envShcoef,
|
|
322
|
+
source: "inline",
|
|
323
|
+
// The environment quantizes SH against its own (wider) range;
|
|
324
|
+
// re-encode into the base range so the whole scene decodes
|
|
325
|
+
// through one pool uniform. See LccShParams.requantizeTo.
|
|
326
|
+
requantizeTo: o.shcoef
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
b.add(v), g += x, $.push({
|
|
332
|
+
bounds: V(o),
|
|
333
|
+
lods: [{ file: v, offset: 0, count: x }]
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const y = {
|
|
337
|
+
leaves: $,
|
|
338
|
+
chunkUrls: d,
|
|
339
|
+
// Per-level splat totals; index 0 is the finest. Informational here (the
|
|
340
|
+
// pool and coverage floor are sized explicitly below), but kept accurate.
|
|
341
|
+
counts: o.splats.slice(0, C),
|
|
342
|
+
lodLevels: C,
|
|
343
|
+
bounds: V(o)
|
|
344
|
+
}, L = await Pt(t);
|
|
345
|
+
return {
|
|
346
|
+
// Classic LCC cells are broad XY tiles. Keep their priority independent of
|
|
347
|
+
// frustum crossings so an orbit does not demote a sharp tile as it leaves
|
|
348
|
+
// one edge, then rebuild it when the same tile re-enters the other edge.
|
|
349
|
+
source: new yt(y, {
|
|
350
|
+
...n,
|
|
351
|
+
// Classic LCC cells are broad XY tiles. Keep their priority independent of
|
|
352
|
+
// frustum crossings so an orbit does not demote a sharp tile as it leaves
|
|
353
|
+
// one edge, then rebuild it when the same tile re-enters the other edge.
|
|
354
|
+
frustumAware: !1,
|
|
355
|
+
// Short range: distance bands pick the level (≤~9 m → L0, ≤20 m → L1,
|
|
356
|
+
// …). Do not blanket-force finest then re-promote after demotion - that
|
|
357
|
+
// loaded L0 for margin cells only to demote them with the camera still.
|
|
358
|
+
forceFinestWithin: void 0,
|
|
359
|
+
// Distance band is the fetch target. Do not fill past it.
|
|
360
|
+
fillPastDistance: !1,
|
|
361
|
+
// XGRIDS retains a little budget headroom and does not turn a raised
|
|
362
|
+
// ceiling into an all-finest distant cut. The cap limits only optional
|
|
363
|
+
// nearest-first restoration after demotion; the distance-selected cut
|
|
364
|
+
// can still spend the caller's full explicit budget when it needs it.
|
|
365
|
+
budgetFillFraction: 0.95,
|
|
366
|
+
budgetFillCap: 8e6,
|
|
367
|
+
forceFinestWhenFits: !1
|
|
368
|
+
}),
|
|
369
|
+
chunkUrls: d,
|
|
370
|
+
chunkOptions: m,
|
|
371
|
+
shBands: f ?? 0,
|
|
372
|
+
chunkKind: "file",
|
|
373
|
+
bounds: V(o),
|
|
374
|
+
pinnedFiles: b,
|
|
375
|
+
// Worst case is every cell resident at its finest level, plus the sky.
|
|
376
|
+
maxResidentSplats: (o.splats[0] ?? n.budget) + x,
|
|
377
|
+
// Full coarse coverage is each cell's coarsest level plus the sky - the
|
|
378
|
+
// budget floor below which even the substitute levels cannot all fit.
|
|
379
|
+
minimumCoverageSplats: Math.max(1, g),
|
|
380
|
+
// Same Z-up frame as .lcc2; bounds stay source-local and matrixWorld
|
|
381
|
+
// handles the conversion.
|
|
382
|
+
formatTransform: q(),
|
|
383
|
+
// Optional classic-LCC collision mesh file. One descriptor for the whole
|
|
384
|
+
// `.lci`; `loadCollisionMeshes` expands it into per-cell triangle tiles.
|
|
385
|
+
...L !== null ? { collision: { meshes: [{ url: L }] } } : {}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
async function Pt(e) {
|
|
389
|
+
for (const t of ["collision.lci", "Collision.lci"]) {
|
|
390
|
+
const n = await e.size(t);
|
|
391
|
+
if (n !== null && Number.isSafeInteger(n) && n > 0)
|
|
392
|
+
return e.resolve(t);
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
function at(e) {
|
|
397
|
+
return e.fileType === "Quality" ? U + O : U;
|
|
398
|
+
}
|
|
399
|
+
function Rt(e, t) {
|
|
400
|
+
const { min: n, max: o } = t.bounds, s = n[0] + e.cellX * t.cellLengthX, i = n[1] + e.cellY * t.cellLengthY;
|
|
401
|
+
return new E.Box3(
|
|
402
|
+
new E.Vector3(Math.min(s, o[0]), Math.min(i, o[1]), n[2]),
|
|
403
|
+
new E.Vector3(
|
|
404
|
+
Math.min(s + t.cellLengthX, o[0]),
|
|
405
|
+
Math.min(i + t.cellLengthY, o[1]),
|
|
406
|
+
o[2]
|
|
407
|
+
)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
function V(e) {
|
|
411
|
+
const { min: t, max: n } = e.bounds;
|
|
412
|
+
return new E.Box3(
|
|
413
|
+
new E.Vector3(t[0], t[1], t[2]),
|
|
414
|
+
new E.Vector3(n[0], n[1], n[2])
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
function Nt(e, t) {
|
|
418
|
+
for (let n = 0; n < t.totalLevel; n++) {
|
|
419
|
+
let o = 0;
|
|
420
|
+
for (const c of e) o += c.levels[n].count;
|
|
421
|
+
const s = t.splats[n], i = s - o, r = Math.max(4, Math.ceil(s * 1e-3));
|
|
422
|
+
if (i < 0 || i > r)
|
|
423
|
+
throw new Error(
|
|
424
|
+
`LCC index.bin level ${n} totals ${o} splats, but the manifest declares ${String(t.splats[n])}.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async function _t(e, t) {
|
|
429
|
+
const n = await e.size("environment.bin");
|
|
430
|
+
if (n === null || !Number.isSafeInteger(n) || n <= 0) return 0;
|
|
431
|
+
const o = at(t);
|
|
432
|
+
if (n % o !== 0)
|
|
433
|
+
throw new Error(
|
|
434
|
+
`LCC environment.bin is ${n} bytes, not a multiple of the ${o}-byte ${t.fileType} record.`
|
|
435
|
+
);
|
|
436
|
+
return n / o;
|
|
437
|
+
}
|
|
438
|
+
async function It(e, t, n) {
|
|
439
|
+
let o;
|
|
440
|
+
try {
|
|
441
|
+
o = await fetch(e, ct(t.request, t.signal));
|
|
442
|
+
} catch (s) {
|
|
443
|
+
throw Q(s, { phase: n, url: e });
|
|
444
|
+
}
|
|
445
|
+
if (!o.ok)
|
|
446
|
+
throw Q(new Error(`Failed to load ${e}: HTTP ${o.status}`), {
|
|
447
|
+
phase: n,
|
|
448
|
+
url: e,
|
|
449
|
+
status: o.status
|
|
450
|
+
});
|
|
451
|
+
return o.arrayBuffer();
|
|
452
|
+
}
|
|
453
|
+
const zt = 3, Dt = 0.1, J = 0.1, Ot = 500;
|
|
454
|
+
function ae(e, t, n, o = 0) {
|
|
455
|
+
var $, b;
|
|
456
|
+
const s = e, i = s.root.splatFiles.map((g) => {
|
|
457
|
+
const C = t.resolve(g);
|
|
458
|
+
if (C === null) throw new Error(`LCC2 dataset is missing the tile "${g}".`);
|
|
459
|
+
return C;
|
|
460
|
+
}), r = [], c = [];
|
|
461
|
+
let u = 0;
|
|
462
|
+
const l = (g, C) => {
|
|
463
|
+
var M;
|
|
464
|
+
const x = (M = g.data) == null ? void 0 : M["3dgs"];
|
|
465
|
+
if (!x) throw new Error("LCC2 node is missing 3dgs data.");
|
|
466
|
+
const y = r.length;
|
|
467
|
+
r.push(null);
|
|
468
|
+
const L = u, w = [], v = g.child ? Object.values(g.child) : [];
|
|
469
|
+
if (v.length === 0)
|
|
470
|
+
c[u] = y, u++;
|
|
471
|
+
else
|
|
472
|
+
for (const S of v) w.push(l(S, C + 1));
|
|
473
|
+
const F = v.length === 0 ? L + 1 : u;
|
|
474
|
+
return r[y] = {
|
|
475
|
+
file: x.name,
|
|
476
|
+
offset: x.start,
|
|
477
|
+
count: x.count,
|
|
478
|
+
bounds: X(g.boundingBox),
|
|
479
|
+
level: s.totalLevels - C,
|
|
480
|
+
children: w,
|
|
481
|
+
leafStart: L,
|
|
482
|
+
leafEnd: F
|
|
483
|
+
}, y;
|
|
484
|
+
}, a = Object.values(s.root.child ?? {}).map((g) => l(g, 1)), f = new Yt(r, a, c, s.totalLevels - 1, n), h = new Set(a.map((g) => r[g].file)), d = (b = ($ = s.root.data) == null ? void 0 : $.env) == null ? void 0 : b.name, m = d !== void 0 && i[d] !== void 0 ? { file: d } : void 0;
|
|
485
|
+
d !== void 0 && m === void 0 && D(`LCC2 root references env tile #${d}, which the manifest does not list.`), m && h.add(m.file);
|
|
486
|
+
const p = o >= 1 ? o : void 0;
|
|
487
|
+
return {
|
|
488
|
+
source: f,
|
|
489
|
+
chunkUrls: i,
|
|
490
|
+
chunkKind: "file",
|
|
491
|
+
// LCC2 tiles are single bundled .sog (ZIP) files
|
|
492
|
+
// Explicit format: a dropped folder resolves tiles to `blob:` URLs with no
|
|
493
|
+
// `.sog` extension, so the chunk loader cannot sniff the parser from the URL.
|
|
494
|
+
chunkOptions: i.map(() => ({
|
|
495
|
+
format: "sog",
|
|
496
|
+
...p ? { sog: { packShBands: p } } : {}
|
|
497
|
+
})),
|
|
498
|
+
bounds: X(s.root.boundingBox),
|
|
499
|
+
pinnedFiles: h,
|
|
500
|
+
maxResidentSplats: s.lodSplats[0] ?? n.budget,
|
|
501
|
+
minimumCoverageSplats: Math.max(
|
|
502
|
+
1,
|
|
503
|
+
a.reduce((g, C) => g + r[C].count, 0)
|
|
504
|
+
),
|
|
505
|
+
...kt(s, t),
|
|
506
|
+
...m ? { environment: m } : {},
|
|
507
|
+
...p ? { shBands: p } : {},
|
|
508
|
+
// Match the established XGRIDS LCC and Spark viewer coordinate frame.
|
|
509
|
+
// Bounds remain source-local and matrixWorld handles the conversion.
|
|
510
|
+
formatTransform: q()
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function kt(e, t) {
|
|
514
|
+
const n = e.root.meshFiles;
|
|
515
|
+
if (!n || n.length === 0) return {};
|
|
516
|
+
const o = /* @__PURE__ */ new Map(), s = (c) => {
|
|
517
|
+
var l;
|
|
518
|
+
const u = (l = c.data) == null ? void 0 : l.mesh;
|
|
519
|
+
u && !o.has(u.name) && o.set(u.name, X(c.boundingBox));
|
|
520
|
+
for (const a of Object.values(c.child ?? {})) s(a);
|
|
521
|
+
};
|
|
522
|
+
s(e.root);
|
|
523
|
+
const i = o.size > 0 ? [...o].map(([c, u]) => ({ file: c, bounds: u })) : n.map((c, u) => ({ file: u })), r = [];
|
|
524
|
+
for (const { file: c, bounds: u } of i) {
|
|
525
|
+
const l = n[c];
|
|
526
|
+
if (l === void 0) {
|
|
527
|
+
D(`LCC2 node references mesh file ${c}, which the manifest does not list.`);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const a = t.resolve(l);
|
|
531
|
+
if (a === null) {
|
|
532
|
+
D(`LCC2 dataset is missing the collision mesh "${l}"; skipping it.`);
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
r.push({ url: a, ...u ? { bounds: u } : {} });
|
|
536
|
+
}
|
|
537
|
+
return r.length > 0 ? { collision: { meshes: r } } : {};
|
|
538
|
+
}
|
|
539
|
+
class Yt {
|
|
540
|
+
constructor(t, n, o, s, i) {
|
|
541
|
+
A(this, "budget");
|
|
542
|
+
A(this, "lodBaseDistance");
|
|
543
|
+
A(this, "lodMultiplier");
|
|
544
|
+
A(this, "nodes");
|
|
545
|
+
A(this, "rootChildren");
|
|
546
|
+
A(this, "cellNodes");
|
|
547
|
+
A(this, "coarsest");
|
|
548
|
+
/** Finest available level per cell (its own node's level). */
|
|
549
|
+
A(this, "cellFinestLevel");
|
|
550
|
+
/** Stable, dwelled desired level per cell. */
|
|
551
|
+
A(this, "cellLevel");
|
|
552
|
+
/** Timestamp of each cell's last level change, for dwell. */
|
|
553
|
+
A(this, "changedAt");
|
|
554
|
+
/** Index into {@link rootChildren} of the subtree containing each cell. */
|
|
555
|
+
A(this, "cellRootChild");
|
|
556
|
+
A(this, "scratchBox", new E.Box3());
|
|
557
|
+
A(this, "scratchSize", new E.Vector3());
|
|
558
|
+
this.nodes = t, this.rootChildren = n, this.cellNodes = o, this.coarsest = s, this.budget = i.budget, this.lodBaseDistance = i.lodBaseDistance, this.lodMultiplier = i.lodMultiplier;
|
|
559
|
+
const r = o.length;
|
|
560
|
+
this.cellFinestLevel = new Int32Array(r), this.cellLevel = new Int32Array(r).fill(s), this.changedAt = new Float64Array(r);
|
|
561
|
+
for (let c = 0; c < r; c++)
|
|
562
|
+
this.cellFinestLevel[c] = t[o[c]].level;
|
|
563
|
+
this.cellRootChild = new Uint32Array(r), n.forEach((c, u) => {
|
|
564
|
+
const l = t[c];
|
|
565
|
+
for (let a = l.leafStart; a < l.leafEnd; a++) this.cellRootChild[a] = u;
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
computeDesiredRuns(t, n, o) {
|
|
569
|
+
const s = this.updateCellLevels(t, n, o), i = Int32Array.from(this.cellLevel), r = this.rootChildren.map((h) => this.subtreeCutTotal(h, i));
|
|
570
|
+
let c = r.reduce((h, d) => h + d, 0);
|
|
571
|
+
const u = (h, d) => {
|
|
572
|
+
i[h] = i[h] + d;
|
|
573
|
+
const m = this.cellRootChild[h], p = this.subtreeCutTotal(this.rootChildren[m], i);
|
|
574
|
+
c += p - r[m], r[m] = p;
|
|
575
|
+
}, l = [...this.cellNodes.keys()].sort(
|
|
576
|
+
(h, d) => s[d] - s[h]
|
|
577
|
+
);
|
|
578
|
+
for (let h = 0; h < l.length && c > this.budget; ) {
|
|
579
|
+
const d = l[h];
|
|
580
|
+
i[d] < this.coarsest ? u(d, 1) : h++;
|
|
581
|
+
}
|
|
582
|
+
const a = this.budget * 0.85;
|
|
583
|
+
for (let h = l.length - 1; h >= 0 && c < a; ) {
|
|
584
|
+
const d = l[h];
|
|
585
|
+
if (i[d] > this.cellFinestLevel[d]) {
|
|
586
|
+
if (u(d, -1), c > this.budget) {
|
|
587
|
+
u(d, 1);
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
} else
|
|
591
|
+
h--;
|
|
592
|
+
}
|
|
593
|
+
const f = [];
|
|
594
|
+
return this.collectCut(i, (h) => f.push(tt(this.nodes[h]))), f;
|
|
595
|
+
}
|
|
596
|
+
coarsestRunsFor(t, n) {
|
|
597
|
+
const o = [];
|
|
598
|
+
for (const s of this.rootChildren) {
|
|
599
|
+
const i = this.nodes[s];
|
|
600
|
+
i.leafStart < n && i.leafEnd > t && o.push(tt(i));
|
|
601
|
+
}
|
|
602
|
+
return o;
|
|
603
|
+
}
|
|
604
|
+
/** Distance (frustum-penalized) to each finest cell's node bounds. */
|
|
605
|
+
cellDistanceOf(t, n, o) {
|
|
606
|
+
const s = this.nodes[this.cellNodes[o]], i = s.bounds.distanceToPoint(t);
|
|
607
|
+
return this.scratchBox.copy(s.bounds), s.bounds.getSize(this.scratchSize), this.scratchBox.expandByScalar(this.scratchSize.length() * Dt), n.intersectsBox(this.scratchBox) ? i : i * zt;
|
|
608
|
+
}
|
|
609
|
+
/** Advances each cell's dwelled level toward the distance target. */
|
|
610
|
+
updateCellLevels(t, n, o) {
|
|
611
|
+
const { lodBaseDistance: s, lodMultiplier: i } = this, r = new Float64Array(this.cellNodes.length);
|
|
612
|
+
for (let c = 0; c < this.cellNodes.length; c++) {
|
|
613
|
+
const u = this.cellDistanceOf(t, n, c);
|
|
614
|
+
r[c] = u;
|
|
615
|
+
const l = this.cellFinestLevel[c];
|
|
616
|
+
let a = Math.min(this.coarsest, Math.max(l, this.cellLevel[c]));
|
|
617
|
+
for (; a < this.coarsest && u > s * i ** a * (1 + J); ) a++;
|
|
618
|
+
for (; a > l && u <= s * i ** (a - 1) * (1 - J); ) a--;
|
|
619
|
+
a !== this.cellLevel[c] && o - this.changedAt[c] >= Ot && (this.cellLevel[c] = a, this.changedAt[c] = o);
|
|
620
|
+
}
|
|
621
|
+
return r;
|
|
622
|
+
}
|
|
623
|
+
/** Splat total of one subtree's cut for the given per-cell levels. */
|
|
624
|
+
subtreeCutTotal(t, n) {
|
|
625
|
+
let o = 0;
|
|
626
|
+
return this.visitCut(t, n, (s) => {
|
|
627
|
+
o += this.nodes[s].count;
|
|
628
|
+
}), o;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Walks the tree emitting the cut for the given per-cell levels: a node is
|
|
632
|
+
* selected (stop) unless some cell in its subtree wants finer than the
|
|
633
|
+
* node provides, in which case its children are visited. Guarantees an
|
|
634
|
+
* antichain - exactly one node per root→leaf path.
|
|
635
|
+
*/
|
|
636
|
+
collectCut(t, n) {
|
|
637
|
+
for (const o of this.rootChildren) this.visitCut(o, t, n);
|
|
638
|
+
}
|
|
639
|
+
/** {@link collectCut} restricted to one subtree. */
|
|
640
|
+
visitCut(t, n, o) {
|
|
641
|
+
const s = this.nodes[t];
|
|
642
|
+
if (s.children.length > 0) {
|
|
643
|
+
let i = this.coarsest;
|
|
644
|
+
for (let r = s.leafStart; r < s.leafEnd; r++) {
|
|
645
|
+
const c = n[r];
|
|
646
|
+
c < i && (i = c);
|
|
647
|
+
}
|
|
648
|
+
if (i < s.level) {
|
|
649
|
+
for (const r of s.children) this.visitCut(r, n, o);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
o(t);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function tt(e) {
|
|
657
|
+
return {
|
|
658
|
+
file: e.file,
|
|
659
|
+
level: e.level,
|
|
660
|
+
offset: e.offset,
|
|
661
|
+
count: e.count,
|
|
662
|
+
leafStart: e.leafStart,
|
|
663
|
+
leafEnd: e.leafEnd
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function X(e) {
|
|
667
|
+
return new E.Box3(
|
|
668
|
+
new E.Vector3(e.min[0], e.min[1], e.min[2]),
|
|
669
|
+
new E.Vector3(e.max[0], e.max[1], e.max[2])
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
const et = ["vertex_indices", "vertex_index"];
|
|
673
|
+
function Vt(e) {
|
|
674
|
+
const t = vt(e), n = t.element("vertex");
|
|
675
|
+
if (!n) throw new Error('Not a mesh PLY: no "vertex" element.');
|
|
676
|
+
xt(n, ["x", "y", "z"], ["float", "float32"], "Mesh PLY vertex"), bt(n, e.byteLength);
|
|
677
|
+
const o = Xt(e, n), s = t.element("face");
|
|
678
|
+
if (!s) throw new Error('Not a mesh PLY: no "face" element.');
|
|
679
|
+
const i = qt(e, s, n.count);
|
|
680
|
+
return {
|
|
681
|
+
vertexCount: n.count,
|
|
682
|
+
triangleCount: i.length / 3,
|
|
683
|
+
positions: o,
|
|
684
|
+
indices: i
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function Xt(e, t) {
|
|
688
|
+
const n = new DataView(e, t.offset), o = k(t, "x"), s = k(t, "y"), i = k(t, "z"), r = new Float32Array(t.count * 3);
|
|
689
|
+
for (let c = 0; c < t.count; c++) {
|
|
690
|
+
const u = c * t.stride;
|
|
691
|
+
r[c * 3] = n.getFloat32(u + o, !0), r[c * 3 + 1] = n.getFloat32(u + s, !0), r[c * 3 + 2] = n.getFloat32(u + i, !0);
|
|
692
|
+
}
|
|
693
|
+
return r;
|
|
694
|
+
}
|
|
695
|
+
function qt(e, t, n) {
|
|
696
|
+
if (t.count === 0) return new Uint32Array(0);
|
|
697
|
+
if (t.offset < 0)
|
|
698
|
+
throw new Error(
|
|
699
|
+
'Unsupported PLY layout: the "face" element follows a list property, so its position cannot be determined.'
|
|
700
|
+
);
|
|
701
|
+
if (t.properties.size > 0)
|
|
702
|
+
throw new Error(
|
|
703
|
+
'Unsupported mesh PLY: the "face" element mixes scalar properties with its index list.'
|
|
704
|
+
);
|
|
705
|
+
const o = et.find((h) => t.listProperties.has(h)), s = o ? t.listProperties.get(o) : void 0;
|
|
706
|
+
if (!s)
|
|
707
|
+
throw new Error(
|
|
708
|
+
`Mesh PLY "face" element is missing an index list (${et.join(" or ")}).`
|
|
709
|
+
);
|
|
710
|
+
const i = W(s.countType), r = W(s.itemType), c = new DataView(e), u = e.byteLength;
|
|
711
|
+
let l = new Uint32Array(t.count * 3), a = 0, f = t.offset;
|
|
712
|
+
for (let h = 0; h < t.count; h++) {
|
|
713
|
+
if (f + i > u) throw new Error(nt(h));
|
|
714
|
+
const d = ot(c, f, s.countType, i);
|
|
715
|
+
if (d < 0)
|
|
716
|
+
throw new Error(`Mesh PLY face ${h} declares a negative vertex count (${d}).`);
|
|
717
|
+
if (f += i, f + d * r > u) throw new Error(nt(h));
|
|
718
|
+
const m = Math.max(0, d - 2);
|
|
719
|
+
if (a + m * 3 > l.length) {
|
|
720
|
+
const b = new Uint32Array(Math.max(l.length * 2, a + m * 3));
|
|
721
|
+
b.set(l.subarray(0, a)), l = b;
|
|
722
|
+
}
|
|
723
|
+
let p = 0, $ = 0;
|
|
724
|
+
for (let b = 0; b < d; b++) {
|
|
725
|
+
const g = ot(c, f + b * r, s.itemType, r);
|
|
726
|
+
if (g < 0 || g >= n)
|
|
727
|
+
throw new Error(
|
|
728
|
+
`Mesh PLY face ${h} references vertex ${g}, but the file has ${n}.`
|
|
729
|
+
);
|
|
730
|
+
b === 0 ? p = g : b >= 2 && (l[a++] = p, l[a++] = $, l[a++] = g), $ = g;
|
|
731
|
+
}
|
|
732
|
+
f += d * r;
|
|
733
|
+
}
|
|
734
|
+
return a === l.length ? l : l.slice(0, a);
|
|
735
|
+
}
|
|
736
|
+
function nt(e) {
|
|
737
|
+
return `Truncated PLY file: the "face" element ends inside face ${e}.`;
|
|
738
|
+
}
|
|
739
|
+
function ot(e, t, n, o) {
|
|
740
|
+
switch (n) {
|
|
741
|
+
case "char":
|
|
742
|
+
case "int8":
|
|
743
|
+
return e.getInt8(t);
|
|
744
|
+
case "uchar":
|
|
745
|
+
case "uint8":
|
|
746
|
+
return e.getUint8(t);
|
|
747
|
+
case "short":
|
|
748
|
+
case "int16":
|
|
749
|
+
return e.getInt16(t, !0);
|
|
750
|
+
case "ushort":
|
|
751
|
+
case "uint16":
|
|
752
|
+
return e.getUint16(t, !0);
|
|
753
|
+
case "int":
|
|
754
|
+
case "int32":
|
|
755
|
+
return e.getInt32(t, !0);
|
|
756
|
+
case "uint":
|
|
757
|
+
case "uint32":
|
|
758
|
+
return e.getUint32(t, !0);
|
|
759
|
+
default:
|
|
760
|
+
throw new Error(`Unsupported PLY index type: "${n}" (${o} bytes).`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const Ht = 1819045731, st = 2, z = 48, it = 40, ut = 3, ht = 3, rt = ut * 4, Gt = ht * 4;
|
|
764
|
+
function Qt(e) {
|
|
765
|
+
if (e.byteLength < z)
|
|
766
|
+
throw new Error(
|
|
767
|
+
`collision.lci is truncated: ${e.byteLength} bytes, need at least ${z}.`
|
|
768
|
+
);
|
|
769
|
+
const t = new DataView(e), n = t.getUint32(0, !0);
|
|
770
|
+
if (n !== Ht)
|
|
771
|
+
throw new Error(
|
|
772
|
+
`Not a collision.lci: expected magic "coll", got 0x${n.toString(16).padStart(8, "0")}.`
|
|
773
|
+
);
|
|
774
|
+
const o = t.getUint32(4, !0);
|
|
775
|
+
if (o !== st)
|
|
776
|
+
throw new Error(
|
|
777
|
+
`Unsupported collision.lci version ${o} (expected ${st}).`
|
|
778
|
+
);
|
|
779
|
+
const s = t.getUint32(8, !0), i = new E.Vector3(
|
|
780
|
+
t.getFloat32(12, !0),
|
|
781
|
+
t.getFloat32(16, !0),
|
|
782
|
+
t.getFloat32(20, !0)
|
|
783
|
+
), r = new E.Vector3(
|
|
784
|
+
t.getFloat32(24, !0),
|
|
785
|
+
t.getFloat32(28, !0),
|
|
786
|
+
t.getFloat32(32, !0)
|
|
787
|
+
), c = t.getFloat32(36, !0), u = t.getFloat32(40, !0), l = t.getUint32(44, !0), a = z + l * it;
|
|
788
|
+
if (s !== a)
|
|
789
|
+
throw new Error(
|
|
790
|
+
`collision.lci headerLen ${s} does not match ${l} mesh headers (expected ${a}).`
|
|
791
|
+
);
|
|
792
|
+
if (e.byteLength < s)
|
|
793
|
+
throw new Error(
|
|
794
|
+
`collision.lci is truncated: header claims ${s} bytes, file has ${e.byteLength}.`
|
|
795
|
+
);
|
|
796
|
+
if (!(c > 0) || !(u > 0))
|
|
797
|
+
throw new Error(
|
|
798
|
+
`collision.lci cell lengths must be positive (got ${c} × ${u}).`
|
|
799
|
+
);
|
|
800
|
+
const f = new E.Box3(i.clone(), r.clone()), h = [];
|
|
801
|
+
let d = s;
|
|
802
|
+
for (let m = 0; m < l; m++) {
|
|
803
|
+
const p = z + m * it, $ = t.getUint32(p, !0), b = t.getUint32(p + 4, !0), g = Number(t.getBigUint64(p + 8, !0)), C = Number(t.getBigUint64(p + 16, !0)), x = t.getUint32(p + 24, !0), y = t.getUint32(p + 28, !0), L = t.getUint32(p + 32, !0);
|
|
804
|
+
if (!Number.isSafeInteger(g) || !Number.isSafeInteger(C))
|
|
805
|
+
throw new Error(`collision.lci mesh ${m} has an unsafe data offset/size.`);
|
|
806
|
+
if (g !== d)
|
|
807
|
+
throw new Error(
|
|
808
|
+
`collision.lci mesh ${m} data offset ${g} is not contiguous (expected ${d}).`
|
|
809
|
+
);
|
|
810
|
+
if (x * rt + y * Gt + L !== C)
|
|
811
|
+
throw new Error(
|
|
812
|
+
`collision.lci mesh ${m} size ${C} != ${x}*12 + ${y}*12 + BVH ${L}.`
|
|
813
|
+
);
|
|
814
|
+
const v = g + C;
|
|
815
|
+
if (v > e.byteLength)
|
|
816
|
+
throw new Error(
|
|
817
|
+
`collision.lci mesh ${m} extends to byte ${v}, past file length ${e.byteLength}.`
|
|
818
|
+
);
|
|
819
|
+
const F = new Float32Array(e, g, x * ut), M = g + x * rt, S = new Uint32Array(e, M, y * ht);
|
|
820
|
+
for (let T = 0; T < S.length; T++) {
|
|
821
|
+
const B = S[T];
|
|
822
|
+
if (B >= x)
|
|
823
|
+
throw new Error(
|
|
824
|
+
`collision.lci mesh ${m} face index ${B} is out of range (vertexCount ${x}).`
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
h.push({
|
|
828
|
+
cellX: $,
|
|
829
|
+
cellY: b,
|
|
830
|
+
data: {
|
|
831
|
+
vertexCount: x,
|
|
832
|
+
triangleCount: y,
|
|
833
|
+
// Copy so the returned arrays outlive a transferred/detached buffer
|
|
834
|
+
// and are not views into the BVH-adjacent region.
|
|
835
|
+
positions: F.slice(),
|
|
836
|
+
indices: S.slice()
|
|
837
|
+
},
|
|
838
|
+
bounds: Wt($, b, c, u, f)
|
|
839
|
+
}), d = v;
|
|
840
|
+
}
|
|
841
|
+
if (d !== e.byteLength)
|
|
842
|
+
throw new Error(
|
|
843
|
+
`collision.lci has ${e.byteLength - d} trailing bytes after the last mesh.`
|
|
844
|
+
);
|
|
845
|
+
return {
|
|
846
|
+
version: o,
|
|
847
|
+
bounds: f,
|
|
848
|
+
cellLengthX: c,
|
|
849
|
+
cellLengthY: u,
|
|
850
|
+
tiles: h
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function Wt(e, t, n, o, s) {
|
|
854
|
+
const i = s.min.x + e * n, r = s.min.y + t * o;
|
|
855
|
+
return new E.Box3(
|
|
856
|
+
new E.Vector3(Math.min(i, s.max.x), Math.min(r, s.max.y), s.min.z),
|
|
857
|
+
new E.Vector3(
|
|
858
|
+
Math.min(i + n, s.max.x),
|
|
859
|
+
Math.min(r + o, s.max.y),
|
|
860
|
+
s.max.z
|
|
861
|
+
)
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
async function ue(e, t = {}) {
|
|
865
|
+
var s;
|
|
866
|
+
const n = await Promise.allSettled(
|
|
867
|
+
e.meshes.map((i) => jt(i, t))
|
|
868
|
+
);
|
|
869
|
+
(s = t.signal) == null || s.throwIfAborted();
|
|
870
|
+
const o = [];
|
|
871
|
+
for (const i of n)
|
|
872
|
+
i.status === "fulfilled" ? o.push(...i.value) : D("Skipping a collision mesh that failed to load:", i.reason);
|
|
873
|
+
return o;
|
|
874
|
+
}
|
|
875
|
+
async function jt(e, t) {
|
|
876
|
+
const n = await fetch(e.url, ct(t.request, t.signal));
|
|
877
|
+
if (!n.ok)
|
|
878
|
+
throw new Error(`HTTP ${n.status} for collision mesh ${e.url}`);
|
|
879
|
+
const o = await n.arrayBuffer();
|
|
880
|
+
if (Kt(e.url))
|
|
881
|
+
return Qt(o).tiles.map((r) => ({
|
|
882
|
+
url: `${e.url}#c${r.cellX}_${r.cellY}`,
|
|
883
|
+
data: r.data,
|
|
884
|
+
bounds: r.bounds
|
|
885
|
+
}));
|
|
886
|
+
const s = Vt(o);
|
|
887
|
+
return [
|
|
888
|
+
{
|
|
889
|
+
url: e.url,
|
|
890
|
+
data: s,
|
|
891
|
+
...e.bounds ? { bounds: e.bounds } : {}
|
|
892
|
+
}
|
|
893
|
+
];
|
|
894
|
+
}
|
|
895
|
+
function Kt(e) {
|
|
896
|
+
try {
|
|
897
|
+
return new URL(e, "https://example.invalid/").pathname.toLowerCase().endsWith(".lci");
|
|
898
|
+
} catch {
|
|
899
|
+
return e.toLowerCase().includes(".lci");
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function he(e, t) {
|
|
903
|
+
Zt(e);
|
|
904
|
+
const { positions: n, indices: o, vertexCount: s, triangleCount: i } = e, r = new Uint8Array(s);
|
|
905
|
+
for (let l = 0; l < s; l++)
|
|
906
|
+
r[l] = t.containsPoint(
|
|
907
|
+
n[l * 3],
|
|
908
|
+
n[l * 3 + 1],
|
|
909
|
+
n[l * 3 + 2]
|
|
910
|
+
) ? 1 : 0;
|
|
911
|
+
const c = new Uint8Array(i);
|
|
912
|
+
let u = 0;
|
|
913
|
+
for (let l = 0; l < i; l++) {
|
|
914
|
+
const a = o[l * 3], f = o[l * 3 + 1], h = o[l * 3 + 2];
|
|
915
|
+
(r[a] === 1 || r[f] === 1 || r[h] === 1) && (c[l] = 1, u++);
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
inside: lt(e, c, 1, u),
|
|
919
|
+
outside: lt(e, c, 0, i - u)
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function Zt(e) {
|
|
923
|
+
if (e.positions.length < e.vertexCount * 3)
|
|
924
|
+
throw new Error(
|
|
925
|
+
`partitionTriangleMesh: positions hold ${e.positions.length} floats, too few for ${e.vertexCount} vertices.`
|
|
926
|
+
);
|
|
927
|
+
if (e.indices.length < e.triangleCount * 3)
|
|
928
|
+
throw new Error(
|
|
929
|
+
`partitionTriangleMesh: indices hold ${e.indices.length} entries, too few for ${e.triangleCount} triangles.`
|
|
930
|
+
);
|
|
931
|
+
for (let t = 0; t < e.triangleCount * 3; t++) {
|
|
932
|
+
const n = e.indices[t];
|
|
933
|
+
if (n >= e.vertexCount)
|
|
934
|
+
throw new Error(
|
|
935
|
+
`partitionTriangleMesh: index ${n} is out of range (${e.vertexCount} vertices).`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
function lt(e, t, n, o) {
|
|
940
|
+
const s = new Int32Array(e.vertexCount).fill(-1), i = new Uint32Array(o * 3);
|
|
941
|
+
let r = 0, c = 0;
|
|
942
|
+
for (let l = 0; l < e.triangleCount; l++)
|
|
943
|
+
if (t[l] === n)
|
|
944
|
+
for (let a = 0; a < 3; a++) {
|
|
945
|
+
const f = e.indices[l * 3 + a];
|
|
946
|
+
let h = s[f];
|
|
947
|
+
h < 0 && (h = r++, s[f] = h), i[c++] = h;
|
|
948
|
+
}
|
|
949
|
+
const u = new Float32Array(r * 3);
|
|
950
|
+
for (let l = 0; l < e.vertexCount; l++) {
|
|
951
|
+
const a = s[l];
|
|
952
|
+
a < 0 || (u[a * 3] = e.positions[l * 3], u[a * 3 + 1] = e.positions[l * 3 + 1], u[a * 3 + 2] = e.positions[l * 3 + 2]);
|
|
953
|
+
}
|
|
954
|
+
return { vertexCount: r, triangleCount: o, positions: u, indices: i };
|
|
955
|
+
}
|
|
956
|
+
export {
|
|
957
|
+
le as applyLcc2ToThreeTransform,
|
|
958
|
+
ae as buildLcc2Scene,
|
|
959
|
+
ce as buildLccScene,
|
|
960
|
+
q as createLcc2ToThreeMatrix,
|
|
961
|
+
ue as loadCollisionMeshTiles,
|
|
962
|
+
Qt as parseCollisionLci,
|
|
963
|
+
re as parseLccChunk,
|
|
964
|
+
Mt as parseLccIndex,
|
|
965
|
+
Et as parseLccManifest,
|
|
966
|
+
Vt as parseMeshPly,
|
|
967
|
+
he as partitionTriangleMesh
|
|
968
|
+
};
|
|
969
|
+
//# sourceMappingURL=lcc.js.map
|