@needle-tools/gltf-progressive 4.0.0-alpha → 4.0.0-alpha.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/gltf-progressive.js +1568 -0
- package/gltf-progressive.min.js +10 -0
- package/gltf-progressive.umd.cjs +10 -0
- package/lib/extension.d.ts +142 -0
- package/lib/extension.js +1133 -0
- package/lib/extension.model.d.ts +33 -0
- package/lib/extension.model.js +1 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +87 -0
- package/lib/loaders.d.ts +41 -0
- package/lib/loaders.js +162 -0
- package/lib/lods.debug.d.ts +4 -0
- package/lib/lods.debug.js +43 -0
- package/lib/lods.manager.d.ts +165 -0
- package/lib/lods.manager.js +749 -0
- package/lib/lods.promise.d.ts +68 -0
- package/lib/lods.promise.js +108 -0
- package/lib/plugins/index.d.ts +2 -0
- package/lib/plugins/index.js +1 -0
- package/lib/plugins/modelviewer.d.ts +1 -0
- package/lib/plugins/modelviewer.js +223 -0
- package/lib/plugins/plugin.d.ts +23 -0
- package/lib/plugins/plugin.js +5 -0
- package/lib/utils.d.ts +30 -0
- package/lib/utils.internal.d.ts +68 -0
- package/lib/utils.internal.js +239 -0
- package/lib/utils.js +82 -0
- package/lib/version.d.ts +1 -0
- package/lib/version.js +4 -0
- package/lib/worker/gltf-progressive.worker.js +165 -0
- package/lib/worker/loader.mainthread.d.ts +45 -0
- package/lib/worker/loader.mainthread.js +192 -0
- package/package.json +4 -17
|
@@ -0,0 +1,1568 @@
|
|
|
1
|
+
import { RedFormat as Ue, RedIntegerFormat as ze, RGFormat as Ne, RGIntegerFormat as qe, RGBFormat as Ve, RGBAFormat as Ee, RGBAIntegerFormat as je, BufferGeometry as E, Mesh as j, Box3 as ge, Vector3 as A, Sphere as Ce, CompressedTexture as Xe, Texture as F, Matrix3 as Ke, InterleavedBuffer as Ye, InterleavedBufferAttribute as He, BufferAttribute as Qe, TextureLoader as Je, Matrix4 as Me, Timer as Ze, MeshStandardMaterial as et } from "three";
|
|
2
|
+
import { GLTFLoader as _e } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
|
+
import { MeshoptDecoder as tt } from "three/examples/jsm/libs/meshopt_decoder.module.js";
|
|
4
|
+
import { DRACOLoader as st } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
5
|
+
import { KTX2Loader as rt } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
6
|
+
const ot = "";
|
|
7
|
+
globalThis.GLTF_PROGRESSIVE_VERSION = ot;
|
|
8
|
+
console.debug("[gltf-progressive] version -");
|
|
9
|
+
let I = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", X = "https://cdn.needle.tools/static/three/0.179.1/basis2/";
|
|
10
|
+
const nt = I, it = X, Ae = new URL(I + "draco_decoder.js");
|
|
11
|
+
Ae.searchParams.append("range", "true");
|
|
12
|
+
fetch(Ae, {
|
|
13
|
+
method: "GET",
|
|
14
|
+
headers: {
|
|
15
|
+
Range: "bytes=0-1"
|
|
16
|
+
}
|
|
17
|
+
}).catch((i) => {
|
|
18
|
+
console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), I === nt && lt("./include/draco/"), X === it && ut("./include/ktx2/");
|
|
19
|
+
}).finally(() => {
|
|
20
|
+
ke();
|
|
21
|
+
});
|
|
22
|
+
const at = () => ({
|
|
23
|
+
dracoDecoderPath: I,
|
|
24
|
+
ktx2TranscoderPath: X
|
|
25
|
+
});
|
|
26
|
+
function lt(i) {
|
|
27
|
+
I = i, C && C[pe] != I ? (console.debug("Updating Draco decoder path to " + i), C[pe] = I, C.setDecoderPath(I), C.preload()) : console.debug("Setting Draco decoder path to " + i);
|
|
28
|
+
}
|
|
29
|
+
function ut(i) {
|
|
30
|
+
X = i, $ && $.transcoderPath != X ? (console.debug("Updating KTX2 transcoder path to " + i), $.setTranscoderPath(X), $.init()) : console.debug("Setting KTX2 transcoder path to " + i);
|
|
31
|
+
}
|
|
32
|
+
function Le(i) {
|
|
33
|
+
return ke(), i ? $.detectSupport(i) : i !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: C, ktx2Loader: $, meshoptDecoder: ne };
|
|
34
|
+
}
|
|
35
|
+
function Re(i) {
|
|
36
|
+
i.dracoLoader || i.setDRACOLoader(C), i.ktx2Loader || i.setKTX2Loader($), i.meshoptDecoder || i.setMeshoptDecoder(ne);
|
|
37
|
+
}
|
|
38
|
+
const pe = /* @__PURE__ */ Symbol("dracoDecoderPath");
|
|
39
|
+
let C, ne, $;
|
|
40
|
+
function ke() {
|
|
41
|
+
C || (C = new st(), C[pe] = I, C.setDecoderPath(I), C.setDecoderConfig({ type: "js" }), C.preload()), $ || ($ = new rt(), $.setTranscoderPath(X), $.init()), ne || (ne = tt);
|
|
42
|
+
}
|
|
43
|
+
const me = /* @__PURE__ */ new WeakMap();
|
|
44
|
+
function Ie(i, t) {
|
|
45
|
+
let e = me.get(i);
|
|
46
|
+
e ? e = Object.assign(e, t) : e = t, me.set(i, e);
|
|
47
|
+
}
|
|
48
|
+
const ct = _e.prototype.load;
|
|
49
|
+
function dt(...i) {
|
|
50
|
+
const t = me.get(this);
|
|
51
|
+
let e = i[0];
|
|
52
|
+
const s = new URL(e, window.location.href);
|
|
53
|
+
if (s.hostname.endsWith("needle.tools")) {
|
|
54
|
+
const o = t?.progressive !== void 0 ? t.progressive : !0, n = t?.usecase ? t.usecase : "default";
|
|
55
|
+
o ? this.requestHeader.Accept = `*/*;progressive=allowed;usecase=${n}` : this.requestHeader.Accept = `*/*;usecase=${n}`, e = s.toString();
|
|
56
|
+
}
|
|
57
|
+
return i[0] = e, ct?.call(this, ...i);
|
|
58
|
+
}
|
|
59
|
+
_e.prototype.load = dt;
|
|
60
|
+
function De(i) {
|
|
61
|
+
return i != null && i.data != null;
|
|
62
|
+
}
|
|
63
|
+
function ye(i) {
|
|
64
|
+
const t = i.source?.data;
|
|
65
|
+
return t != null && typeof t == "object" ? t : null;
|
|
66
|
+
}
|
|
67
|
+
function ft(i) {
|
|
68
|
+
const t = i.image;
|
|
69
|
+
return t != null && typeof t == "object" ? t : null;
|
|
70
|
+
}
|
|
71
|
+
function xe(i) {
|
|
72
|
+
const t = ft(i), e = ye(i);
|
|
73
|
+
return {
|
|
74
|
+
width: t?.width || e?.width || 0,
|
|
75
|
+
height: t?.height || e?.height || 0
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
N("debugprogressive");
|
|
79
|
+
function N(i) {
|
|
80
|
+
if (typeof window > "u") return !1;
|
|
81
|
+
const e = new URL(window.location.href).searchParams.get(i);
|
|
82
|
+
return e == null || e === "0" || e === "false" ? !1 : e === "" ? !0 : e;
|
|
83
|
+
}
|
|
84
|
+
function ht(i, t) {
|
|
85
|
+
if (t === void 0 || i === void 0 || t.startsWith("./") || t.startsWith("http") || t.startsWith("data:") || t.startsWith("blob:"))
|
|
86
|
+
return t;
|
|
87
|
+
const e = i.lastIndexOf("/");
|
|
88
|
+
if (e >= 0) {
|
|
89
|
+
const s = i.substring(0, e + 1);
|
|
90
|
+
for (; s.endsWith("/") && t.startsWith("/"); ) t = t.substring(1);
|
|
91
|
+
return s + t;
|
|
92
|
+
}
|
|
93
|
+
return t;
|
|
94
|
+
}
|
|
95
|
+
function ve() {
|
|
96
|
+
return H !== void 0 || (H = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), N("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", H)), H;
|
|
97
|
+
}
|
|
98
|
+
let H;
|
|
99
|
+
function Oe() {
|
|
100
|
+
if (typeof window > "u") return !1;
|
|
101
|
+
const i = new URL(window.location.href), t = i.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);
|
|
102
|
+
return i.hostname === "127.0.0.1" || t;
|
|
103
|
+
}
|
|
104
|
+
class gt {
|
|
105
|
+
constructor(t, e = {}) {
|
|
106
|
+
this.maxConcurrent = t, this.debug = e.debug ?? !1, window.requestAnimationFrame(this.tick);
|
|
107
|
+
}
|
|
108
|
+
_running = /* @__PURE__ */ new Map();
|
|
109
|
+
_queue = [];
|
|
110
|
+
debug = !1;
|
|
111
|
+
tick = () => {
|
|
112
|
+
this.internalUpdate(), setTimeout(this.tick, 10);
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Request a slot for a promise with a specific key. This function returns a promise with a `use` method that can be called to add the promise to the queue.
|
|
116
|
+
*/
|
|
117
|
+
slot(t) {
|
|
118
|
+
return this.debug && console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`), new Promise((e) => {
|
|
119
|
+
this._queue.push({ key: t, resolve: e });
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
add(t, e) {
|
|
123
|
+
this._running.has(t) || (this._running.set(t, e), e.finally(() => {
|
|
124
|
+
this._running.delete(t), this.debug && console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`);
|
|
125
|
+
}), this.debug && console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`));
|
|
126
|
+
}
|
|
127
|
+
internalUpdate() {
|
|
128
|
+
const t = this.maxConcurrent - this._running.size;
|
|
129
|
+
for (let e = 0; e < t && this._queue.length > 0; e++) {
|
|
130
|
+
this.debug && console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);
|
|
131
|
+
const { key: s, resolve: r } = this._queue.shift();
|
|
132
|
+
r({
|
|
133
|
+
use: (o) => this.add(s, o)
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function pt(i) {
|
|
139
|
+
const t = i.image, e = t?.width ?? 0, s = t?.height ?? 0, r = t?.depth ?? 1, o = Math.floor(Math.log2(Math.max(e, s, r))) + 1, n = mt(i);
|
|
140
|
+
return e * s * r * n * (1 - Math.pow(0.25, o)) / (1 - 0.25);
|
|
141
|
+
}
|
|
142
|
+
function mt(i) {
|
|
143
|
+
let t = 4;
|
|
144
|
+
const e = i.format;
|
|
145
|
+
e === Ue || e === ze ? t = 1 : e === Ne || e === qe ? t = 2 : e === Ve || e === 1029 ? t = 3 : (e === Ee || e === je) && (t = 4);
|
|
146
|
+
let s = 1;
|
|
147
|
+
const r = i.type;
|
|
148
|
+
return r === 1009 || r === 1010 ? s = 1 : r === 1011 || r === 1012 ? s = 2 : r === 1013 || r === 1014 || r === 1015 ? s = 4 : r === 1016 && (s = 2), t * s;
|
|
149
|
+
}
|
|
150
|
+
const yt = typeof window > "u" && typeof document > "u", we = /* @__PURE__ */ Symbol("needle:raycast-mesh");
|
|
151
|
+
function ee(i) {
|
|
152
|
+
return i?.[we] instanceof E ? i[we] : null;
|
|
153
|
+
}
|
|
154
|
+
function xt(i, t) {
|
|
155
|
+
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !ee(i)) {
|
|
156
|
+
const s = _t(t);
|
|
157
|
+
s.userData = { isRaycastMesh: !0 }, i[we] = s;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function wt(i = !0) {
|
|
161
|
+
if (i) {
|
|
162
|
+
if (Q) return;
|
|
163
|
+
const t = Q = j.prototype.raycast;
|
|
164
|
+
j.prototype.raycast = function(e, s) {
|
|
165
|
+
const r = this, o = ee(r);
|
|
166
|
+
let n;
|
|
167
|
+
o && r.isMesh && (n = r.geometry, r.geometry = o), t.call(this, e, s), n && (r.geometry = n);
|
|
168
|
+
};
|
|
169
|
+
} else {
|
|
170
|
+
if (!Q) return;
|
|
171
|
+
j.prototype.raycast = Q, Q = null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
let Q = null;
|
|
175
|
+
function _t(i) {
|
|
176
|
+
const t = new E();
|
|
177
|
+
for (const e in i.attributes)
|
|
178
|
+
t.setAttribute(e, i.getAttribute(e));
|
|
179
|
+
return t.setIndex(i.getIndex()), t;
|
|
180
|
+
}
|
|
181
|
+
const z = new Array(), f = N("debugprogressive");
|
|
182
|
+
let Z, V = -1;
|
|
183
|
+
if (f) {
|
|
184
|
+
let i = function() {
|
|
185
|
+
V += 1, V >= t && (V = -1), console.log(`Toggle LOD level [${V}]`);
|
|
186
|
+
}, t = 6;
|
|
187
|
+
window.addEventListener("keyup", (e) => {
|
|
188
|
+
e.key === "p" && i(), e.key === "w" && (Z = !Z, console.log(`Toggle wireframe [${Z}]`));
|
|
189
|
+
const s = parseInt(e.key);
|
|
190
|
+
!isNaN(s) && s >= 0 && (V = s, console.log(`Set LOD level to [${V}]`));
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function $e(i) {
|
|
194
|
+
if (f && Z !== void 0)
|
|
195
|
+
if (Array.isArray(i))
|
|
196
|
+
for (const t of i)
|
|
197
|
+
$e(t);
|
|
198
|
+
else i && "wireframe" in i && (i.wireframe = Z === !0);
|
|
199
|
+
}
|
|
200
|
+
const J = new Array();
|
|
201
|
+
let Lt = 0;
|
|
202
|
+
const vt = ve() ? 2 : 10;
|
|
203
|
+
function bt(i) {
|
|
204
|
+
if (J.length < vt) {
|
|
205
|
+
const s = J.length;
|
|
206
|
+
f && console.warn(`[Worker] Creating new worker #${s}`);
|
|
207
|
+
const r = be.createWorker(i || {});
|
|
208
|
+
return J.push(r), r;
|
|
209
|
+
}
|
|
210
|
+
const t = Lt++ % J.length;
|
|
211
|
+
return J[t];
|
|
212
|
+
}
|
|
213
|
+
class be {
|
|
214
|
+
constructor(t, e) {
|
|
215
|
+
this.worker = t, this._debug = e.debug ?? !1, t.onmessage = (s) => {
|
|
216
|
+
const r = s.data;
|
|
217
|
+
switch (this._debug && console.log("[Worker] EVENT", r), r.type) {
|
|
218
|
+
case "loaded-gltf":
|
|
219
|
+
for (const o of this._running)
|
|
220
|
+
if (o.url === r.result.url) {
|
|
221
|
+
Mt(r.result), o.resolve(r.result);
|
|
222
|
+
const n = o.url;
|
|
223
|
+
n.startsWith("blob:") && URL.revokeObjectURL(n);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}, t.onerror = (s) => {
|
|
227
|
+
console.error("[Worker] Error in gltf-progressive worker:", s);
|
|
228
|
+
}, t.postMessage({
|
|
229
|
+
type: "init"
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
static async createWorker(t) {
|
|
233
|
+
const e = new Worker(new URL(
|
|
234
|
+
/* @vite-ignore */
|
|
235
|
+
"/assets/gltf-progressive.worker-CDSrhw-p.js",
|
|
236
|
+
import.meta.url
|
|
237
|
+
), {
|
|
238
|
+
type: "module"
|
|
239
|
+
});
|
|
240
|
+
return new be(e, t);
|
|
241
|
+
}
|
|
242
|
+
_running = [];
|
|
243
|
+
_webglRenderer = null;
|
|
244
|
+
async load(t, e) {
|
|
245
|
+
const s = at();
|
|
246
|
+
let r = e?.renderer;
|
|
247
|
+
r || (this._webglRenderer ??= (async () => {
|
|
248
|
+
const { WebGLRenderer: u } = await import("three");
|
|
249
|
+
return new u();
|
|
250
|
+
})(), r = await this._webglRenderer);
|
|
251
|
+
const l = Le(r).ktx2Loader.workerConfig;
|
|
252
|
+
t instanceof URL ? t = t.toString() : t.startsWith("file:") ? t = URL.createObjectURL(new Blob([t])) : !t.startsWith("blob:") && !t.startsWith("http:") && !t.startsWith("https:") && (t = new URL(t, window.location.href).toString());
|
|
253
|
+
const a = {
|
|
254
|
+
type: "load",
|
|
255
|
+
url: t,
|
|
256
|
+
dracoDecoderPath: s.dracoDecoderPath,
|
|
257
|
+
ktx2TranscoderPath: s.ktx2TranscoderPath,
|
|
258
|
+
ktx2LoaderConfig: l
|
|
259
|
+
};
|
|
260
|
+
return this._debug && console.debug("[Worker] Sending load request", a), this.worker.postMessage(a), new Promise((u) => {
|
|
261
|
+
this._running.push({
|
|
262
|
+
url: t.toString(),
|
|
263
|
+
resolve: u
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
_debug = !1;
|
|
268
|
+
}
|
|
269
|
+
function Mt(i) {
|
|
270
|
+
for (const t of i.geometries) {
|
|
271
|
+
const e = t.geometry, s = new E();
|
|
272
|
+
if (s.name = e.name || "", e.index) {
|
|
273
|
+
const r = e.index;
|
|
274
|
+
s.setIndex(le(r));
|
|
275
|
+
}
|
|
276
|
+
for (const r in e.attributes) {
|
|
277
|
+
const o = e.attributes[r], n = le(o);
|
|
278
|
+
s.setAttribute(r, n);
|
|
279
|
+
}
|
|
280
|
+
if (e.morphAttributes)
|
|
281
|
+
for (const r in e.morphAttributes) {
|
|
282
|
+
const n = e.morphAttributes[r].map((l) => le(l));
|
|
283
|
+
s.morphAttributes[r] = n;
|
|
284
|
+
}
|
|
285
|
+
if (s.morphTargetsRelative = e.morphTargetsRelative ?? !1, s.boundingBox = new ge(), s.boundingBox.min = new A(
|
|
286
|
+
e.boundingBox?.min.x,
|
|
287
|
+
e.boundingBox?.min.y,
|
|
288
|
+
e.boundingBox?.min.z
|
|
289
|
+
), s.boundingBox.max = new A(
|
|
290
|
+
e.boundingBox?.max.x,
|
|
291
|
+
e.boundingBox?.max.y,
|
|
292
|
+
e.boundingBox?.max.z
|
|
293
|
+
), s.boundingSphere = new Ce(
|
|
294
|
+
new A(
|
|
295
|
+
e.boundingSphere?.center.x,
|
|
296
|
+
e.boundingSphere?.center.y,
|
|
297
|
+
e.boundingSphere?.center.z
|
|
298
|
+
),
|
|
299
|
+
e.boundingSphere?.radius
|
|
300
|
+
), e.groups)
|
|
301
|
+
for (const r of e.groups)
|
|
302
|
+
s.addGroup(r.start, r.count, r.materialIndex);
|
|
303
|
+
e.userData && (s.userData = e.userData), t.geometry = s;
|
|
304
|
+
}
|
|
305
|
+
for (const t of i.textures) {
|
|
306
|
+
const e = t.texture;
|
|
307
|
+
let s = null;
|
|
308
|
+
if (e.isCompressedTexture) {
|
|
309
|
+
const r = e.mipmaps, { width: o, height: n } = xe(e);
|
|
310
|
+
s = new Xe(
|
|
311
|
+
r,
|
|
312
|
+
o,
|
|
313
|
+
n,
|
|
314
|
+
e.format,
|
|
315
|
+
e.type,
|
|
316
|
+
e.mapping,
|
|
317
|
+
e.wrapS,
|
|
318
|
+
e.wrapT,
|
|
319
|
+
e.magFilter,
|
|
320
|
+
e.minFilter,
|
|
321
|
+
e.anisotropy,
|
|
322
|
+
e.colorSpace
|
|
323
|
+
);
|
|
324
|
+
} else
|
|
325
|
+
s = new F(
|
|
326
|
+
e.image,
|
|
327
|
+
e.mapping,
|
|
328
|
+
e.wrapS,
|
|
329
|
+
e.wrapT,
|
|
330
|
+
e.magFilter,
|
|
331
|
+
e.minFilter,
|
|
332
|
+
e.format,
|
|
333
|
+
e.type,
|
|
334
|
+
e.anisotropy,
|
|
335
|
+
e.colorSpace
|
|
336
|
+
), s.mipmaps = e.mipmaps, s.channel = e.channel, s.source.data = e.source.data, s.flipY = e.flipY, s.premultiplyAlpha = e.premultiplyAlpha, s.unpackAlignment = e.unpackAlignment, s.matrix = new Ke(...e.matrix.elements);
|
|
337
|
+
if (!s) {
|
|
338
|
+
console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
t.texture = s;
|
|
342
|
+
}
|
|
343
|
+
return i;
|
|
344
|
+
}
|
|
345
|
+
function le(i) {
|
|
346
|
+
let t = i;
|
|
347
|
+
if ("isInterleavedBufferAttribute" in i && i.isInterleavedBufferAttribute) {
|
|
348
|
+
const e = i.data, s = e.array, r = new Ye(s, e.stride);
|
|
349
|
+
t = new He(r, i.itemSize, s.byteOffset, i.normalized), t.offset = i.offset;
|
|
350
|
+
} else "isBufferAttribute" in i && i.isBufferAttribute && (t = new Qe(i.array, i.itemSize, i.normalized), t.usage = i.usage, t.gpuType = i.gpuType, t.updateRanges = i.updateRanges);
|
|
351
|
+
return t;
|
|
352
|
+
}
|
|
353
|
+
const Dt = N("gltf-progressive-worker");
|
|
354
|
+
N("gltf-progressive-reduce-mipmaps");
|
|
355
|
+
const K = N("gltf-progressive-gc"), ue = /* @__PURE__ */ Symbol("needle-progressive-texture"), W = "NEEDLE_progressive";
|
|
356
|
+
class m {
|
|
357
|
+
/** The name of the extension */
|
|
358
|
+
get name() {
|
|
359
|
+
return W;
|
|
360
|
+
}
|
|
361
|
+
// #region PUBLIC API
|
|
362
|
+
static getMeshLODExtension(t) {
|
|
363
|
+
const e = this.getAssignedLODInformation(t);
|
|
364
|
+
return e?.key ? this.lodInfos.get(e.key) : null;
|
|
365
|
+
}
|
|
366
|
+
static getPrimitiveIndex(t) {
|
|
367
|
+
const e = this.getAssignedLODInformation(t)?.index;
|
|
368
|
+
return e ?? -1;
|
|
369
|
+
}
|
|
370
|
+
static getMaterialMinMaxLODsCount(t, e) {
|
|
371
|
+
const s = this, r = "LODS:minmax", o = t[r];
|
|
372
|
+
if (o != null) return o;
|
|
373
|
+
if (e || (e = {
|
|
374
|
+
min_count: 1 / 0,
|
|
375
|
+
max_count: 0,
|
|
376
|
+
lods: []
|
|
377
|
+
}), Array.isArray(t)) {
|
|
378
|
+
for (const l of t)
|
|
379
|
+
this.getMaterialMinMaxLODsCount(l, e);
|
|
380
|
+
return t[r] = e, e;
|
|
381
|
+
}
|
|
382
|
+
if (f === "verbose" && console.log("getMaterialMinMaxLODsCount", t), t.type === "ShaderMaterial" || t.type === "RawShaderMaterial") {
|
|
383
|
+
const l = t;
|
|
384
|
+
for (const a of Object.keys(l.uniforms)) {
|
|
385
|
+
const u = l.uniforms[a].value;
|
|
386
|
+
u?.isTexture === !0 && n(u, e);
|
|
387
|
+
}
|
|
388
|
+
} else if (t.isMaterial)
|
|
389
|
+
for (const l of Object.keys(t)) {
|
|
390
|
+
const a = t[l];
|
|
391
|
+
a?.isTexture === !0 && n(a, e);
|
|
392
|
+
}
|
|
393
|
+
else
|
|
394
|
+
f && console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);
|
|
395
|
+
return t[r] = e, e;
|
|
396
|
+
function n(l, a) {
|
|
397
|
+
const u = s.getAssignedLODInformation(l);
|
|
398
|
+
if (u) {
|
|
399
|
+
const c = s.lodInfos.get(u.key);
|
|
400
|
+
if (c && c.lods) {
|
|
401
|
+
a.min_count = Math.min(a.min_count, c.lods.length), a.max_count = Math.max(a.max_count, c.lods.length);
|
|
402
|
+
for (let y = 0; y < c.lods.length; y++) {
|
|
403
|
+
const w = c.lods[y];
|
|
404
|
+
w.width && (a.lods[y] = a.lods[y] || { min_height: 1 / 0, max_height: 0 }, a.lods[y].min_height = Math.min(a.lods[y].min_height, w.height), a.lods[y].max_height = Math.max(a.lods[y].max_height, w.height));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** Check if a LOD level is available for a mesh or a texture
|
|
411
|
+
* @param obj the mesh or texture to check
|
|
412
|
+
* @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
|
|
413
|
+
* @returns true if the LOD level is available (or if any LOD level is available if level is undefined)
|
|
414
|
+
*/
|
|
415
|
+
static hasLODLevelAvailable(t, e) {
|
|
416
|
+
if (Array.isArray(t)) {
|
|
417
|
+
for (const o of t)
|
|
418
|
+
if (this.hasLODLevelAvailable(o, e)) return !0;
|
|
419
|
+
return !1;
|
|
420
|
+
}
|
|
421
|
+
if (t.isMaterial === !0) {
|
|
422
|
+
for (const o of Object.keys(t)) {
|
|
423
|
+
const n = t[o];
|
|
424
|
+
if (n && n.isTexture && this.hasLODLevelAvailable(n, e))
|
|
425
|
+
return !0;
|
|
426
|
+
}
|
|
427
|
+
return !1;
|
|
428
|
+
} else if (t.isGroup === !0) {
|
|
429
|
+
for (const o of t.children)
|
|
430
|
+
if (o.isMesh === !0 && this.hasLODLevelAvailable(o, e))
|
|
431
|
+
return !0;
|
|
432
|
+
}
|
|
433
|
+
let s, r;
|
|
434
|
+
if (t.isMesh ? s = t.geometry : (t.isBufferGeometry || t.isTexture) && (s = t), s && s?.userData?.LODS) {
|
|
435
|
+
const o = s.userData.LODS;
|
|
436
|
+
if (r = this.lodInfos.get(o.key), e === void 0) return r != null;
|
|
437
|
+
if (r)
|
|
438
|
+
return Array.isArray(r.lods) ? e < r.lods.length : e === 0;
|
|
439
|
+
}
|
|
440
|
+
return !1;
|
|
441
|
+
}
|
|
442
|
+
/** Load a different resolution of a mesh (if available)
|
|
443
|
+
* @param context the context
|
|
444
|
+
* @param source the sourceid of the file from which the mesh is loaded (this is usually the component's sourceId)
|
|
445
|
+
* @param mesh the mesh to load the LOD for
|
|
446
|
+
* @param level the level of detail to load (0 is the highest resolution)
|
|
447
|
+
* @returns a promise that resolves to the mesh with the requested LOD level
|
|
448
|
+
* @example
|
|
449
|
+
* ```javascript
|
|
450
|
+
* const mesh = this.gameObject as Mesh;
|
|
451
|
+
* NEEDLE_progressive.assignMeshLOD(context, sourceId, mesh, 1).then(mesh => {
|
|
452
|
+
* console.log("Mesh with LOD level 1 loaded", mesh);
|
|
453
|
+
* });
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
static assignMeshLOD(t, e) {
|
|
457
|
+
if (!t) return Promise.resolve(null);
|
|
458
|
+
if (t instanceof j || t.isMesh === !0) {
|
|
459
|
+
const s = t.geometry, r = this.getAssignedLODInformation(s);
|
|
460
|
+
if (!r)
|
|
461
|
+
return Promise.resolve(null);
|
|
462
|
+
for (const o of z)
|
|
463
|
+
o.onBeforeGetLODMesh?.(t, e);
|
|
464
|
+
return t["LOD:requested level"] = e, m.getOrLoadLOD(s, e).then((o) => {
|
|
465
|
+
if (Array.isArray(o)) {
|
|
466
|
+
const n = r.index || 0;
|
|
467
|
+
o = o[n];
|
|
468
|
+
}
|
|
469
|
+
return t["LOD:requested level"] === e && (delete t["LOD:requested level"], o && s != o && (o?.isBufferGeometry ? t.geometry = o : f && console.error("Invalid LOD geometry", o))), o;
|
|
470
|
+
}).catch((o) => (console.error("Error loading mesh LOD", t, o), null));
|
|
471
|
+
} else f && console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh", t);
|
|
472
|
+
return Promise.resolve(null);
|
|
473
|
+
}
|
|
474
|
+
static assignTextureLOD(t, e = 0) {
|
|
475
|
+
if (!t) return Promise.resolve(null);
|
|
476
|
+
if (t.isMesh === !0) {
|
|
477
|
+
const s = t;
|
|
478
|
+
if (Array.isArray(s.material)) {
|
|
479
|
+
const r = new Array();
|
|
480
|
+
for (const o of s.material) {
|
|
481
|
+
const n = this.assignTextureLOD(o, e);
|
|
482
|
+
r.push(n);
|
|
483
|
+
}
|
|
484
|
+
return Promise.all(r).then((o) => {
|
|
485
|
+
const n = new Array();
|
|
486
|
+
for (const l of o)
|
|
487
|
+
Array.isArray(l) && n.push(...l);
|
|
488
|
+
return n;
|
|
489
|
+
});
|
|
490
|
+
} else
|
|
491
|
+
return this.assignTextureLOD(s.material, e);
|
|
492
|
+
}
|
|
493
|
+
if (t.isMaterial === !0) {
|
|
494
|
+
const s = t, r = [], o = new Array();
|
|
495
|
+
if (s.uniforms && (s.isRawShaderMaterial || s.isShaderMaterial === !0)) {
|
|
496
|
+
const n = s;
|
|
497
|
+
for (const l of Object.keys(n.uniforms)) {
|
|
498
|
+
const a = n.uniforms[l].value;
|
|
499
|
+
if (a?.isTexture === !0) {
|
|
500
|
+
const u = this.assignTextureLODForSlot(a, e, s, l).then((c) => (c && n.uniforms[l].value != c && (n.uniforms[l].value = c, n.uniformsNeedUpdate = !0), c));
|
|
501
|
+
r.push(u), o.push(l);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
} else
|
|
505
|
+
for (const n of Object.keys(s)) {
|
|
506
|
+
const l = s[n];
|
|
507
|
+
if (l?.isTexture === !0) {
|
|
508
|
+
const a = this.assignTextureLODForSlot(l, e, s, n);
|
|
509
|
+
r.push(a), o.push(n);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return Promise.all(r).then((n) => {
|
|
513
|
+
const l = new Array();
|
|
514
|
+
for (let a = 0; a < n.length; a++) {
|
|
515
|
+
const u = n[a], c = o[a];
|
|
516
|
+
u && u.isTexture === !0 ? l.push({ material: s, slot: c, texture: u, level: e }) : l.push({ material: s, slot: c, texture: null, level: e });
|
|
517
|
+
}
|
|
518
|
+
return l;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
if (t instanceof F || t.isTexture === !0) {
|
|
522
|
+
const s = t;
|
|
523
|
+
return this.assignTextureLODForSlot(s, e, null, null);
|
|
524
|
+
}
|
|
525
|
+
return Promise.resolve(null);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Set the maximum number of concurrent loading tasks for LOD resources. This limits how many LOD resources (meshes or textures) can be loaded at the same time to prevent overloading the network or GPU. If the limit is reached, additional loading requests will be queued and processed as previous ones finish.
|
|
529
|
+
* @default 50 on desktop, 20 on mobile devices
|
|
530
|
+
*/
|
|
531
|
+
static set maxConcurrentLoadingTasks(t) {
|
|
532
|
+
m.queue.maxConcurrent = t;
|
|
533
|
+
}
|
|
534
|
+
static get maxConcurrentLoadingTasks() {
|
|
535
|
+
return m.queue.maxConcurrent;
|
|
536
|
+
}
|
|
537
|
+
// #region INTERNAL
|
|
538
|
+
static assignTextureLODForSlot(t, e, s, r) {
|
|
539
|
+
return t?.isTexture !== !0 ? Promise.resolve(null) : r === "glyphMap" ? Promise.resolve(t) : m.getOrLoadLOD(t, e).then((o) => {
|
|
540
|
+
if (Array.isArray(o))
|
|
541
|
+
return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."), null;
|
|
542
|
+
if (o?.isTexture === !0) {
|
|
543
|
+
if (o != t && s && r) {
|
|
544
|
+
const n = s[r];
|
|
545
|
+
if (n && !f) {
|
|
546
|
+
const l = this.getAssignedLODInformation(n);
|
|
547
|
+
if (l && l?.level < e)
|
|
548
|
+
return f === "verbose" && console.warn("Assigned texture level is already higher: ", l.level, e, s, n, o), o && o !== n && ((f || K) && console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`, o.uuid), o.dispose()), null;
|
|
549
|
+
}
|
|
550
|
+
if (this.trackTextureUsage(o), n && n !== o && this.untrackTextureUsage(n) && (f || K)) {
|
|
551
|
+
const a = this.getAssignedLODInformation(n);
|
|
552
|
+
console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level ?? "?"} → ${e} for ${s.name || s.type}.${r}`, n.uuid);
|
|
553
|
+
}
|
|
554
|
+
s[r] = o;
|
|
555
|
+
}
|
|
556
|
+
return o;
|
|
557
|
+
} else f == "verbose" && console.warn("No LOD found for", t, e);
|
|
558
|
+
return null;
|
|
559
|
+
}).catch((o) => (console.error("Error loading LOD", t, o), null));
|
|
560
|
+
}
|
|
561
|
+
parser;
|
|
562
|
+
url;
|
|
563
|
+
constructor(t) {
|
|
564
|
+
const e = t.options.path;
|
|
565
|
+
f && console.log("Progressive extension registered for", e), this.parser = t, this.url = e;
|
|
566
|
+
}
|
|
567
|
+
_isLoadingMesh;
|
|
568
|
+
loadMesh = (t) => {
|
|
569
|
+
if (this._isLoadingMesh) return null;
|
|
570
|
+
const e = this.parser.json.meshes[t]?.extensions?.[W];
|
|
571
|
+
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && m.registerMesh(this.url, e.guid, s, e.lods?.length, 0, e), s))) : null;
|
|
572
|
+
};
|
|
573
|
+
// private _isLoadingTexture;
|
|
574
|
+
// loadTexture = (textureIndex: number) => {
|
|
575
|
+
// if (this._isLoadingTexture) return null;
|
|
576
|
+
// const ext = this.parser.json.textures[textureIndex]?.extensions?.[EXTENSION_NAME] as NEEDLE_ext_progressive_texture;
|
|
577
|
+
// if (!ext) return null;
|
|
578
|
+
// this._isLoadingTexture = true;
|
|
579
|
+
// return this.parser.getDependency("texture", textureIndex).then(tex => {
|
|
580
|
+
// this._isLoadingTexture = false;
|
|
581
|
+
// if (tex) {
|
|
582
|
+
// NEEDLE_progressive.registerTexture(this.url, tex as Texture, ext.lods?.length, textureIndex, ext);
|
|
583
|
+
// }
|
|
584
|
+
// return tex;
|
|
585
|
+
// });
|
|
586
|
+
// }
|
|
587
|
+
afterRoot(t) {
|
|
588
|
+
return f && console.log("AFTER", this.url, t), this.parser.json.textures?.forEach((e, s) => {
|
|
589
|
+
if (e?.extensions) {
|
|
590
|
+
const r = e?.extensions[W];
|
|
591
|
+
if (r) {
|
|
592
|
+
if (!r.lods) {
|
|
593
|
+
f && console.warn("Texture has no LODs", r);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
let o = !1;
|
|
597
|
+
for (const n of this.parser.associations.keys())
|
|
598
|
+
n.isTexture === !0 && this.parser.associations.get(n)?.textures === s && (o = !0, m.registerTexture(this.url, n, r.lods?.length, s, r));
|
|
599
|
+
o || this.parser.getDependency("texture", s).then((n) => {
|
|
600
|
+
n && m.registerTexture(this.url, n, r.lods?.length, s, r);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}), this.parser.json.meshes?.forEach((e, s) => {
|
|
605
|
+
if (e?.extensions) {
|
|
606
|
+
const r = e?.extensions[W];
|
|
607
|
+
if (r && r.lods) {
|
|
608
|
+
for (const o of this.parser.associations.keys())
|
|
609
|
+
if (o.isMesh) {
|
|
610
|
+
const n = this.parser.associations.get(o);
|
|
611
|
+
n?.meshes === s && m.registerMesh(this.url, r.guid, o, r.lods.length, n.primitives, r);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}), null;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Register a texture with LOD information
|
|
619
|
+
*/
|
|
620
|
+
static registerTexture = (t, e, s, r, o) => {
|
|
621
|
+
if (!e) {
|
|
622
|
+
f && console.error("!! gltf-progressive: Called register texture without texture");
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (f) {
|
|
626
|
+
const { width: l, height: a } = xe(e);
|
|
627
|
+
console.log(`> gltf-progressive: register texture[${r}] "${e.name || e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`, o, e);
|
|
628
|
+
}
|
|
629
|
+
e.source && (e.source[ue] = o);
|
|
630
|
+
const n = o.guid;
|
|
631
|
+
m.assignLODInformation(t, e, n, s, r), m.lodInfos.set(n, o), m.lowresCache.set(n, new WeakRef(e));
|
|
632
|
+
};
|
|
633
|
+
/**
|
|
634
|
+
* Register a mesh with LOD information
|
|
635
|
+
*/
|
|
636
|
+
static registerMesh = (t, e, s, r, o, n) => {
|
|
637
|
+
const l = s.geometry;
|
|
638
|
+
if (!l) {
|
|
639
|
+
f && console.warn("gltf-progressive: Register mesh without geometry");
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
l.userData || (l.userData = {}), f && console.log("> Progressive: register mesh " + s.name, { index: o, uuid: s.uuid }, n, s), m.assignLODInformation(t, l, e, r, o), m.lodInfos.set(e, n);
|
|
643
|
+
let u = m.lowresCache.get(e)?.deref();
|
|
644
|
+
u ? u.push(s.geometry) : u = [s.geometry], m.lowresCache.set(e, new WeakRef(u)), r > 0 && !ee(s) && xt(s, l);
|
|
645
|
+
for (const c of z)
|
|
646
|
+
c.onRegisteredNewMesh?.(s, n);
|
|
647
|
+
};
|
|
648
|
+
/**
|
|
649
|
+
* Dispose cached resources to free memory.
|
|
650
|
+
* Call this when a model is removed from the scene to allow garbage collection of its LOD resources.
|
|
651
|
+
* Calls three.js `.dispose()` on cached Textures and BufferGeometries to free GPU memory.
|
|
652
|
+
* Also clears reference counts for disposed textures.
|
|
653
|
+
* @param guid Optional GUID to dispose resources for a specific model. If omitted, all cached resources are cleared.
|
|
654
|
+
*/
|
|
655
|
+
static dispose(t) {
|
|
656
|
+
if (t) {
|
|
657
|
+
this.lodInfos.delete(t);
|
|
658
|
+
const e = this.lowresCache.get(t);
|
|
659
|
+
if (e) {
|
|
660
|
+
const s = e.deref();
|
|
661
|
+
if (s) {
|
|
662
|
+
if (s.isTexture) {
|
|
663
|
+
const r = s;
|
|
664
|
+
this.textureRefCounts.delete(r.uuid), r.dispose();
|
|
665
|
+
} else if (Array.isArray(s))
|
|
666
|
+
for (const r of s) r.dispose();
|
|
667
|
+
}
|
|
668
|
+
this.lowresCache.delete(t);
|
|
669
|
+
}
|
|
670
|
+
for (const [s, r] of this.cache)
|
|
671
|
+
s.includes(t) && (this._disposeCacheEntry(r), this.cache.delete(s));
|
|
672
|
+
} else {
|
|
673
|
+
this.lodInfos.clear();
|
|
674
|
+
for (const [, e] of this.lowresCache) {
|
|
675
|
+
const s = e.deref();
|
|
676
|
+
if (s) {
|
|
677
|
+
if (s.isTexture) {
|
|
678
|
+
const r = s;
|
|
679
|
+
this.textureRefCounts.delete(r.uuid), r.dispose();
|
|
680
|
+
} else if (Array.isArray(s))
|
|
681
|
+
for (const r of s) r.dispose();
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
this.lowresCache.clear();
|
|
685
|
+
for (const [, e] of this.cache)
|
|
686
|
+
this._disposeCacheEntry(e);
|
|
687
|
+
this.cache.clear(), this.textureRefCounts.clear();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
/** Dispose a single cache entry's three.js resource(s) to free GPU memory. */
|
|
691
|
+
static _disposeCacheEntry(t) {
|
|
692
|
+
if (t instanceof WeakRef) {
|
|
693
|
+
const e = t.deref();
|
|
694
|
+
e && (e.isTexture && this.textureRefCounts.delete(e.uuid), e.dispose());
|
|
695
|
+
} else
|
|
696
|
+
t.then((e) => {
|
|
697
|
+
if (e)
|
|
698
|
+
if (Array.isArray(e))
|
|
699
|
+
for (const s of e) s.dispose();
|
|
700
|
+
else
|
|
701
|
+
e.isTexture && this.textureRefCounts.delete(e.uuid), e.dispose();
|
|
702
|
+
}).catch(() => {
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
/** A map of key = asset uuid and value = LOD information */
|
|
706
|
+
static lodInfos = /* @__PURE__ */ new Map();
|
|
707
|
+
/** cache of already loaded mesh lods. Uses WeakRef for single resources to allow garbage collection when unused. */
|
|
708
|
+
static cache = /* @__PURE__ */ new Map();
|
|
709
|
+
/** this contains the geometry/textures that were originally loaded. Uses WeakRef to allow garbage collection when unused. */
|
|
710
|
+
static lowresCache = /* @__PURE__ */ new Map();
|
|
711
|
+
/** Reference counting for textures to track usage across multiple materials/objects */
|
|
712
|
+
static textureRefCounts = /* @__PURE__ */ new Map();
|
|
713
|
+
/**
|
|
714
|
+
* FinalizationRegistry to automatically clean up `previouslyLoaded` cache entries
|
|
715
|
+
* when their associated three.js resources are garbage collected by the browser.
|
|
716
|
+
* The held value is the cache key string used in `previouslyLoaded`.
|
|
717
|
+
*/
|
|
718
|
+
static _resourceRegistry = new FinalizationRegistry((t) => {
|
|
719
|
+
const e = m.cache.get(t);
|
|
720
|
+
(f || K) && console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
721
|
+
${t}`), e instanceof WeakRef && (e.deref() || (m.cache.delete(t), (f || K) && console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")));
|
|
722
|
+
});
|
|
723
|
+
/**
|
|
724
|
+
* Track texture usage by incrementing reference count
|
|
725
|
+
*/
|
|
726
|
+
static trackTextureUsage(t) {
|
|
727
|
+
const e = t.uuid, s = this.textureRefCounts.get(e) || 0;
|
|
728
|
+
this.textureRefCounts.set(e, s + 1), f === "verbose" && console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s + 1}`);
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Untrack texture usage by decrementing reference count.
|
|
732
|
+
* Automatically disposes the texture when reference count reaches zero.
|
|
733
|
+
* @returns true if the texture was disposed, false otherwise
|
|
734
|
+
*/
|
|
735
|
+
static untrackTextureUsage(t) {
|
|
736
|
+
const e = t.uuid, s = this.textureRefCounts.get(e);
|
|
737
|
+
if (!s)
|
|
738
|
+
return (f === "verbose" || K) && o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)", 0), t.dispose(), !0;
|
|
739
|
+
const r = s - 1;
|
|
740
|
+
if (r <= 0)
|
|
741
|
+
return this.textureRefCounts.delete(e), (f || K) && o("[gltf-progressive] Memory: Dispose texture", r), t.dispose(), !0;
|
|
742
|
+
return this.textureRefCounts.set(e, r), f === "verbose" && o("[gltf-progressive] Memory: Untrack texture", r), !1;
|
|
743
|
+
function o(n, l) {
|
|
744
|
+
let { width: a, height: u } = xe(t);
|
|
745
|
+
const c = a && u ? `${a}x${u}` : "N/A";
|
|
746
|
+
let y = "N/A";
|
|
747
|
+
a && u && (y = `~${(pt(t) / (1024 * 1024)).toFixed(2)} MB`), console.log(`${n} — ${t.name} ${c} (${y}), refCount: ${s} → ${l}
|
|
748
|
+
${e}`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
static workers = [];
|
|
752
|
+
static _workersIndex = 0;
|
|
753
|
+
static async getOrLoadLOD(t, e) {
|
|
754
|
+
const s = f == "verbose", r = this.getAssignedLODInformation(t);
|
|
755
|
+
if (!r)
|
|
756
|
+
return f && console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`, t), null;
|
|
757
|
+
const o = r?.key;
|
|
758
|
+
let n;
|
|
759
|
+
if (t.isTexture === !0) {
|
|
760
|
+
const a = t;
|
|
761
|
+
a.source && a.source[ue] && (n = a.source[ue]);
|
|
762
|
+
}
|
|
763
|
+
if (n || (n = m.lodInfos.get(o)), !n)
|
|
764
|
+
f && console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`, t.type, m.lodInfos);
|
|
765
|
+
else {
|
|
766
|
+
if (e > 0) {
|
|
767
|
+
let c = !1;
|
|
768
|
+
const y = Array.isArray(n.lods);
|
|
769
|
+
if (y && e >= n.lods.length ? c = !0 : y || (c = !0), c) {
|
|
770
|
+
const w = this.lowresCache.get(o);
|
|
771
|
+
if (w) {
|
|
772
|
+
const _ = w.deref();
|
|
773
|
+
if (_) return _;
|
|
774
|
+
this.lowresCache.delete(o), f && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`);
|
|
775
|
+
}
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const a = Array.isArray(n.lods) ? n.lods[e]?.path : n.lods;
|
|
780
|
+
if (!a)
|
|
781
|
+
return f && !n["missing:uri"] && (n["missing:uri"] = !0, console.warn("Missing uri for progressive asset for LOD " + e, n)), null;
|
|
782
|
+
const u = ht(r.url, a);
|
|
783
|
+
if (u.endsWith(".glb") || u.endsWith(".gltf")) {
|
|
784
|
+
if (!n.guid)
|
|
785
|
+
return console.warn("missing pointer for glb/gltf texture", n), null;
|
|
786
|
+
const c = u + "_" + n.guid, y = await this.queue.slot(u), w = this.cache.get(c);
|
|
787
|
+
if (w !== void 0)
|
|
788
|
+
if (s && console.log(`LOD ${e} was already loading/loaded: ${c}`), w instanceof WeakRef) {
|
|
789
|
+
const d = w.deref();
|
|
790
|
+
if (d) {
|
|
791
|
+
let g = d, b = !1;
|
|
792
|
+
if (g instanceof F && t instanceof F ? De(g.image) || ye(g) ? g = this.copySettings(t, g) : b = !0 : g instanceof E && t instanceof E && (g.attributes.position?.array || (b = !0)), !b)
|
|
793
|
+
return g;
|
|
794
|
+
}
|
|
795
|
+
this.cache.delete(c), f && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${c}`);
|
|
796
|
+
} else {
|
|
797
|
+
let d = await w.catch((b) => (console.error(`Error loading LOD ${e} from ${u}
|
|
798
|
+
`, b), null)), g = !1;
|
|
799
|
+
if (d == null || (d instanceof F && t instanceof F ? De(d.image) || ye(d) ? d = this.copySettings(t, d) : (g = !0, this.cache.delete(c)) : d instanceof E && t instanceof E && (d.attributes.position?.array || (g = !0, this.cache.delete(c)))), !g)
|
|
800
|
+
return d;
|
|
801
|
+
}
|
|
802
|
+
if (!y.use)
|
|
803
|
+
return f && console.log(`LOD ${e} was aborted: ${u}`), null;
|
|
804
|
+
const _ = n, R = new Promise(async (d, g) => {
|
|
805
|
+
if (Dt) {
|
|
806
|
+
const x = await (await bt({})).load(u);
|
|
807
|
+
if (x.textures.length > 0)
|
|
808
|
+
for (const h of x.textures) {
|
|
809
|
+
let p = h.texture;
|
|
810
|
+
return m.assignLODInformation(r.url, p, o, e, void 0), t instanceof F && (p = this.copySettings(t, p)), p && (p.guid = _.guid), d(p);
|
|
811
|
+
}
|
|
812
|
+
if (x.geometries.length > 0) {
|
|
813
|
+
const h = new Array();
|
|
814
|
+
for (const p of x.geometries) {
|
|
815
|
+
const O = p.geometry;
|
|
816
|
+
m.assignLODInformation(r.url, O, o, e, p.primitiveIndex), h.push(O);
|
|
817
|
+
}
|
|
818
|
+
return d(h);
|
|
819
|
+
}
|
|
820
|
+
return d(null);
|
|
821
|
+
}
|
|
822
|
+
const b = new _e();
|
|
823
|
+
Re(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
|
|
824
|
+
let B = u;
|
|
825
|
+
if (_ && Array.isArray(_.lods)) {
|
|
826
|
+
const L = _.lods[e];
|
|
827
|
+
L.hash && (B += "?v=" + L.hash);
|
|
828
|
+
}
|
|
829
|
+
const D = await b.loadAsync(B).catch((L) => (console.error(`Error loading LOD ${e} from ${u}
|
|
830
|
+
`, L), d(null)));
|
|
831
|
+
if (!D)
|
|
832
|
+
return d(null);
|
|
833
|
+
const q = D.parser;
|
|
834
|
+
s && console.log("Loading finished " + u, _.guid);
|
|
835
|
+
let T = 0;
|
|
836
|
+
if (D.parser.json.textures) {
|
|
837
|
+
let L = !1;
|
|
838
|
+
for (const x of D.parser.json.textures) {
|
|
839
|
+
if (x?.extensions) {
|
|
840
|
+
const h = x?.extensions[W];
|
|
841
|
+
if (h?.guid && h.guid === _.guid) {
|
|
842
|
+
L = !0;
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
T++;
|
|
847
|
+
}
|
|
848
|
+
if (L) {
|
|
849
|
+
let x = await q.getDependency("texture", T);
|
|
850
|
+
return x && m.assignLODInformation(r.url, x, o, e, void 0), s && console.log('change "' + t.name + '" → "' + x.name + '"', u, T, x, c), t instanceof F && (x = this.copySettings(t, x)), x && (x.guid = _.guid), d(x);
|
|
851
|
+
} else f && console.warn("Could not find texture with guid", _.guid, D.parser.json);
|
|
852
|
+
}
|
|
853
|
+
if (T = 0, D.parser.json.meshes) {
|
|
854
|
+
let L = !1;
|
|
855
|
+
for (const x of D.parser.json.meshes) {
|
|
856
|
+
if (x?.extensions) {
|
|
857
|
+
const h = x?.extensions[W];
|
|
858
|
+
if (h?.guid && h.guid === _.guid) {
|
|
859
|
+
L = !0;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
T++;
|
|
864
|
+
}
|
|
865
|
+
if (L) {
|
|
866
|
+
const x = await q.getDependency("mesh", T);
|
|
867
|
+
if (s && console.log(`Loaded Mesh "${x.name}"`, u, T, x, c), x.isMesh === !0) {
|
|
868
|
+
const h = x.geometry;
|
|
869
|
+
return m.assignLODInformation(r.url, h, o, e, 0), d(h);
|
|
870
|
+
} else {
|
|
871
|
+
const h = new Array();
|
|
872
|
+
for (let p = 0; p < x.children.length; p++) {
|
|
873
|
+
const O = x.children[p];
|
|
874
|
+
if (O.isMesh === !0) {
|
|
875
|
+
const S = O.geometry;
|
|
876
|
+
m.assignLODInformation(r.url, S, o, e, p), h.push(S);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return d(h);
|
|
880
|
+
}
|
|
881
|
+
} else f && console.warn("Could not find mesh with guid", _.guid, D.parser.json);
|
|
882
|
+
}
|
|
883
|
+
return d(null);
|
|
884
|
+
});
|
|
885
|
+
this.cache.set(c, R), y.use(R);
|
|
886
|
+
const M = await R;
|
|
887
|
+
return M != null ? M instanceof F ? (this.cache.set(c, new WeakRef(M)), m._resourceRegistry.register(M, c)) : Array.isArray(M) ? this.cache.set(c, Promise.resolve(M)) : this.cache.set(c, Promise.resolve(M)) : this.cache.set(c, Promise.resolve(null)), M;
|
|
888
|
+
} else if (t instanceof F) {
|
|
889
|
+
s && console.log("Load texture from uri: " + u);
|
|
890
|
+
const y = await new Je().loadAsync(u);
|
|
891
|
+
return y ? (y.guid = n.guid, y.flipY = !1, y.needsUpdate = !0, y.colorSpace = t.colorSpace, s && console.log(n, y)) : f && console.warn("failed loading", u), y;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
static queue = new gt(ve() ? 20 : 50, { debug: f != !1 });
|
|
897
|
+
static assignLODInformation(t, e, s, r, o) {
|
|
898
|
+
if (!e) return;
|
|
899
|
+
e.userData || (e.userData = {});
|
|
900
|
+
const n = new Ot(t, s, r, o);
|
|
901
|
+
e.userData.LODS = n, "source" in e && typeof e.source == "object" && (e.source.LODS = n);
|
|
902
|
+
}
|
|
903
|
+
static getAssignedLODInformation(t) {
|
|
904
|
+
return t ? t.userData?.LODS ? t.userData.LODS : "source" in t && t.source?.LODS ? t.source.LODS : null : null;
|
|
905
|
+
}
|
|
906
|
+
// private static readonly _copiedTextures: WeakMap<Texture, Texture> = new Map();
|
|
907
|
+
static copySettings(t, e) {
|
|
908
|
+
return e ? (f === "verbose" && console.debug(`Copy texture settings
|
|
909
|
+
`, t.uuid, `
|
|
910
|
+
`, e.uuid), e = e.clone(), e.offset = t.offset, e.repeat = t.repeat, e.colorSpace = t.colorSpace, e.magFilter = t.magFilter, e.minFilter = t.minFilter, e.wrapS = t.wrapS, e.wrapT = t.wrapT, e.flipY = t.flipY, e.anisotropy = t.anisotropy, e.mipmaps || (e.generateMipmaps = t.generateMipmaps), e) : t;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
class Ot {
|
|
914
|
+
url;
|
|
915
|
+
/** the key to lookup the LOD information */
|
|
916
|
+
key;
|
|
917
|
+
level;
|
|
918
|
+
/** For multi objects (e.g. a group of meshes) this is the index of the object */
|
|
919
|
+
index;
|
|
920
|
+
constructor(t, e, s, r) {
|
|
921
|
+
this.url = t, this.key = e, this.level = s, r != null && (this.index = r);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
class ce {
|
|
925
|
+
static addPromise = (t, e, s, r) => {
|
|
926
|
+
r.forEach((o) => {
|
|
927
|
+
o.add(t, e, s);
|
|
928
|
+
});
|
|
929
|
+
};
|
|
930
|
+
ready;
|
|
931
|
+
/** The number of promises that have been added to this group so far */
|
|
932
|
+
get awaitedCount() {
|
|
933
|
+
return this._addedCount;
|
|
934
|
+
}
|
|
935
|
+
/** The number of promises that have been resolved */
|
|
936
|
+
get resolvedCount() {
|
|
937
|
+
return this._resolvedCount;
|
|
938
|
+
}
|
|
939
|
+
/** The number of promises that are in-flight */
|
|
940
|
+
get currentlyAwaiting() {
|
|
941
|
+
return this._awaiting.length;
|
|
942
|
+
}
|
|
943
|
+
_resolve;
|
|
944
|
+
_signal;
|
|
945
|
+
/** start frame can be undefined if the user configured this group to wait for the first promise.
|
|
946
|
+
* Then the start frame will be set when the first promise has been added to the group */
|
|
947
|
+
_frame_start;
|
|
948
|
+
/** How many frames to capture since the start frame */
|
|
949
|
+
_frames_to_capture;
|
|
950
|
+
_resolved = !1;
|
|
951
|
+
_addedCount = 0;
|
|
952
|
+
_resolvedCount = 0;
|
|
953
|
+
/** These promises are currently being awaited */
|
|
954
|
+
_awaiting = [];
|
|
955
|
+
_maxPromisesPerObject = 1;
|
|
956
|
+
constructor(t, e) {
|
|
957
|
+
const r = Math.max(e.frames ?? 2, 2);
|
|
958
|
+
this._frame_start = e.waitForFirstCapture ? void 0 : t, this._frames_to_capture = r, this.ready = new Promise((o) => {
|
|
959
|
+
this._resolve = o;
|
|
960
|
+
}), this.ready.finally(() => {
|
|
961
|
+
this._resolved = !0, this._awaiting.length = 0;
|
|
962
|
+
}), this._signal = e.signal, this._signal?.addEventListener("abort", () => {
|
|
963
|
+
this.resolveNow();
|
|
964
|
+
}), this._maxPromisesPerObject = Math.max(1, e.maxPromisesPerObject ?? 1);
|
|
965
|
+
}
|
|
966
|
+
_currentFrame = 0;
|
|
967
|
+
update(t) {
|
|
968
|
+
this._currentFrame = t, this._frame_start === void 0 && this._addedCount > 0 && (this._frame_start = t), (this._signal?.aborted || this._awaiting.length === 0 && this._frame_start !== void 0 && t > this._frame_start + this._frames_to_capture) && this.resolveNow();
|
|
969
|
+
}
|
|
970
|
+
_seen = /* @__PURE__ */ new WeakMap();
|
|
971
|
+
add(t, e, s) {
|
|
972
|
+
if (this._resolved) {
|
|
973
|
+
f && console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
if (!(this._frame_start !== void 0 && this._currentFrame > this._frame_start + this._frames_to_capture)) {
|
|
977
|
+
if (this._maxPromisesPerObject >= 1)
|
|
978
|
+
if (this._seen.has(e)) {
|
|
979
|
+
let r = this._seen.get(e);
|
|
980
|
+
if (r >= this._maxPromisesPerObject) {
|
|
981
|
+
f && console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
this._seen.set(e, r + 1);
|
|
985
|
+
} else
|
|
986
|
+
this._seen.set(e, 1);
|
|
987
|
+
this._awaiting.push(s), this._addedCount++, s.finally(() => {
|
|
988
|
+
this._resolvedCount++, this._awaiting.splice(this._awaiting.indexOf(s), 1);
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
resolveNow() {
|
|
993
|
+
this._resolved || this._resolve?.({
|
|
994
|
+
awaited_count: this._addedCount,
|
|
995
|
+
resolved_count: this._resolvedCount,
|
|
996
|
+
cancelled: this._signal?.aborted ?? !1
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
const k = N("debugprogressive"), St = N("noprogressive"), de = /* @__PURE__ */ Symbol("Needle:LODSManager"), fe = /* @__PURE__ */ Symbol("Needle:LODState"), U = /* @__PURE__ */ Symbol("Needle:CurrentLOD"), P = { mesh_lod: -1, texture_lod: -1 };
|
|
1001
|
+
class v {
|
|
1002
|
+
/**
|
|
1003
|
+
* Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
|
|
1004
|
+
*/
|
|
1005
|
+
static debugDrawLine;
|
|
1006
|
+
/** @internal */
|
|
1007
|
+
static getObjectLODState(t) {
|
|
1008
|
+
return t[fe];
|
|
1009
|
+
}
|
|
1010
|
+
static addPlugin(t) {
|
|
1011
|
+
z.push(t);
|
|
1012
|
+
}
|
|
1013
|
+
static removePlugin(t) {
|
|
1014
|
+
const e = z.indexOf(t);
|
|
1015
|
+
e >= 0 && z.splice(e, 1);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
|
|
1019
|
+
* @param renderer The renderer to get the LODsManager for.
|
|
1020
|
+
* @returns The LODsManager instance.
|
|
1021
|
+
*/
|
|
1022
|
+
static get(t, e) {
|
|
1023
|
+
if (t[de])
|
|
1024
|
+
return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[de];
|
|
1025
|
+
const s = new v(t, {
|
|
1026
|
+
engine: "unknown",
|
|
1027
|
+
...e
|
|
1028
|
+
});
|
|
1029
|
+
return t[de] = s, s;
|
|
1030
|
+
}
|
|
1031
|
+
renderer;
|
|
1032
|
+
context;
|
|
1033
|
+
projectionScreenMatrix = new Me();
|
|
1034
|
+
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
1035
|
+
get plugins() {
|
|
1036
|
+
return z;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Force override the LOD level for all objects (meshes + textures) rendered in the scene
|
|
1040
|
+
* @default undefined automatically calculate LOD level
|
|
1041
|
+
*/
|
|
1042
|
+
overrideLodLevel = void 0;
|
|
1043
|
+
/**
|
|
1044
|
+
* The target triangle density is the desired max amount of triangles on screen when the mesh is filling the screen.
|
|
1045
|
+
* @default 200_000
|
|
1046
|
+
*/
|
|
1047
|
+
targetTriangleDensity = 2e5;
|
|
1048
|
+
/**
|
|
1049
|
+
* The interval in frames to automatically update the bounds of skinned meshes.
|
|
1050
|
+
* Set to 0 or a negative value to disable automatic bounds updates.
|
|
1051
|
+
* @default 30
|
|
1052
|
+
*/
|
|
1053
|
+
skinnedMeshAutoUpdateBoundsInterval = 30;
|
|
1054
|
+
/**
|
|
1055
|
+
* The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 2, the LODs will be updated every second frame, etc.
|
|
1056
|
+
* @default "auto"
|
|
1057
|
+
*/
|
|
1058
|
+
updateInterval = "auto";
|
|
1059
|
+
#e = 1;
|
|
1060
|
+
/**
|
|
1061
|
+
* If set to true, the LODsManager will not update the LODs.
|
|
1062
|
+
* @default false
|
|
1063
|
+
*/
|
|
1064
|
+
pause = !1;
|
|
1065
|
+
/**
|
|
1066
|
+
* When set to true the LODsManager will not update the LODs. This can be used to manually update the LODs using the `update` method.
|
|
1067
|
+
* Otherwise the LODs will be updated automatically when the renderer renders the scene.
|
|
1068
|
+
* @default false
|
|
1069
|
+
*/
|
|
1070
|
+
manual = !1;
|
|
1071
|
+
_newPromiseGroups = [];
|
|
1072
|
+
_promiseGroupIds = 0;
|
|
1073
|
+
/**
|
|
1074
|
+
* Call to await LODs loading during the next render cycle.
|
|
1075
|
+
*/
|
|
1076
|
+
awaitLoading(t) {
|
|
1077
|
+
const e = this._promiseGroupIds++, s = new ce(this.#r, { ...t });
|
|
1078
|
+
this._newPromiseGroups.push(s);
|
|
1079
|
+
const r = performance.now();
|
|
1080
|
+
return s.ready.finally(() => {
|
|
1081
|
+
const o = this._newPromiseGroups.indexOf(s);
|
|
1082
|
+
o >= 0 && (this._newPromiseGroups.splice(o, 1), Oe() && performance.measure("LODsManager:awaitLoading", {
|
|
1083
|
+
start: r,
|
|
1084
|
+
detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
|
|
1085
|
+
}));
|
|
1086
|
+
}), s.ready;
|
|
1087
|
+
}
|
|
1088
|
+
_postprocessPromiseGroups() {
|
|
1089
|
+
if (this._newPromiseGroups.length !== 0)
|
|
1090
|
+
for (let t = this._newPromiseGroups.length - 1; t >= 0; t--)
|
|
1091
|
+
this._newPromiseGroups[t].update(this.#r);
|
|
1092
|
+
}
|
|
1093
|
+
_lodchangedlisteners = [];
|
|
1094
|
+
addEventListener(t, e) {
|
|
1095
|
+
t === "changed" && this._lodchangedlisteners.push(e);
|
|
1096
|
+
}
|
|
1097
|
+
removeEventListener(t, e) {
|
|
1098
|
+
if (t === "changed") {
|
|
1099
|
+
const s = this._lodchangedlisteners.indexOf(e);
|
|
1100
|
+
s >= 0 && this._lodchangedlisteners.splice(s, 1);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
// readonly plugins: NEEDLE_progressive_plugin[] = [];
|
|
1104
|
+
constructor(t, e) {
|
|
1105
|
+
this.renderer = t, this.context = { ...e };
|
|
1106
|
+
}
|
|
1107
|
+
#t;
|
|
1108
|
+
#n = new Ze();
|
|
1109
|
+
#r = 0;
|
|
1110
|
+
#o = 0;
|
|
1111
|
+
#i = 0;
|
|
1112
|
+
#s = 0;
|
|
1113
|
+
_fpsBuffer = [60, 60, 60, 60, 60];
|
|
1114
|
+
/**
|
|
1115
|
+
* Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
|
|
1116
|
+
*/
|
|
1117
|
+
enable() {
|
|
1118
|
+
if (this.#t) return;
|
|
1119
|
+
console.debug("[gltf-progressive] Enabling LODsManager for renderer");
|
|
1120
|
+
let t = 0;
|
|
1121
|
+
this.#t = this.renderer.render;
|
|
1122
|
+
const e = this;
|
|
1123
|
+
Le(this.renderer), this.renderer.render = function(s, r) {
|
|
1124
|
+
const o = e.renderer.getRenderTarget();
|
|
1125
|
+
(o == null || "isXRRenderTarget" in o && o.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n.update(), e.#o = e.#n.getDelta(), e.#i += e.#o, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#o), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length, k && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
|
|
1126
|
+
const n = t++;
|
|
1127
|
+
e.#t.call(this, s, r), e.onAfterRender(s, r, n);
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
disable() {
|
|
1131
|
+
this.#t && (console.debug("[gltf-progressive] Disabling LODsManager for renderer"), this.renderer.render = this.#t, this.#t = void 0);
|
|
1132
|
+
}
|
|
1133
|
+
update(t, e) {
|
|
1134
|
+
this.internalUpdate(t, e);
|
|
1135
|
+
}
|
|
1136
|
+
onAfterRender(t, e, s) {
|
|
1137
|
+
if (this.pause) return;
|
|
1138
|
+
const o = this.renderer.renderLists.get(t, 0).opaque;
|
|
1139
|
+
let n = !0;
|
|
1140
|
+
if (o.length === 1) {
|
|
1141
|
+
const l = o[0].material;
|
|
1142
|
+
(l.name === "EffectMaterial" || l.name === "CopyShader") && (n = !1);
|
|
1143
|
+
}
|
|
1144
|
+
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (n = !1), n) {
|
|
1145
|
+
if (St || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, k && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, k && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
|
|
1146
|
+
return;
|
|
1147
|
+
this.internalUpdate(t, e), this._postprocessPromiseGroups();
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Update LODs in a scene
|
|
1152
|
+
*/
|
|
1153
|
+
internalUpdate(t, e) {
|
|
1154
|
+
const s = this.renderer.renderLists.get(t, 0), r = s.opaque;
|
|
1155
|
+
this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix, e.matrixWorldInverse);
|
|
1156
|
+
const o = this.targetTriangleDensity;
|
|
1157
|
+
for (const a of r) {
|
|
1158
|
+
if (a.material && (a.geometry?.type === "BoxGeometry" || a.geometry?.type === "BufferGeometry") && (a.material.name === "SphericalGaussianBlur" || a.material.name == "BackgroundCubeMaterial" || a.material.name === "CubemapFromEquirect" || a.material.name === "EquirectangularToCubeUV")) {
|
|
1159
|
+
k && (a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] || (a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] = !0, console.warn("Ignoring skybox or BLIT object", a, a.material.name, a.material.type)));
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
switch (a.material.type) {
|
|
1163
|
+
case "LineBasicMaterial":
|
|
1164
|
+
case "LineDashedMaterial":
|
|
1165
|
+
case "PointsMaterial":
|
|
1166
|
+
case "ShadowMaterial":
|
|
1167
|
+
case "MeshDistanceMaterial":
|
|
1168
|
+
case "MeshDepthMaterial":
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (k === "color" && a.material && !a.object.progressive_debug_color) {
|
|
1172
|
+
a.object.progressive_debug_color = !0;
|
|
1173
|
+
const c = Math.random() * 16777215, y = new et({ color: c });
|
|
1174
|
+
a.object.material = y;
|
|
1175
|
+
}
|
|
1176
|
+
const u = a.object;
|
|
1177
|
+
(u instanceof j || u.isMesh) && this.updateLODs(t, e, u, o);
|
|
1178
|
+
}
|
|
1179
|
+
const n = s.transparent;
|
|
1180
|
+
for (const a of n) {
|
|
1181
|
+
const u = a.object;
|
|
1182
|
+
(u instanceof j || u.isMesh) && this.updateLODs(t, e, u, o);
|
|
1183
|
+
}
|
|
1184
|
+
const l = s.transmissive;
|
|
1185
|
+
for (const a of l) {
|
|
1186
|
+
const u = a.object;
|
|
1187
|
+
(u instanceof j || u.isMesh) && this.updateLODs(t, e, u, o);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
/** Update the LOD levels for the renderer. */
|
|
1191
|
+
updateLODs(t, e, s, r) {
|
|
1192
|
+
s.userData || (s.userData = {});
|
|
1193
|
+
let o = s[fe];
|
|
1194
|
+
if (o || (o = new Tt(), s[fe] = o), o.frames++ < 2)
|
|
1195
|
+
return;
|
|
1196
|
+
for (const l of z)
|
|
1197
|
+
l.onBeforeUpdateLOD?.(this.renderer, t, e, s);
|
|
1198
|
+
const n = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : V;
|
|
1199
|
+
n >= 0 ? (P.mesh_lod = n, P.texture_lod = n) : (this.calculateLodLevel(e, s, o, r, P), P.mesh_lod = Math.round(P.mesh_lod), P.texture_lod = Math.round(P.texture_lod)), P.mesh_lod >= 0 && this.loadProgressiveMeshes(s, P.mesh_lod), s.material && P.texture_lod >= 0 && this.loadProgressiveTextures(s.material, P.texture_lod, n), f && s.material && !s.isGizmo && $e(s.material);
|
|
1200
|
+
for (const l of z)
|
|
1201
|
+
l.onAfterUpdatedLOD?.(this.renderer, t, e, s, P);
|
|
1202
|
+
o.lastLodLevel_Mesh = P.mesh_lod, o.lastLodLevel_Texture = P.texture_lod;
|
|
1203
|
+
}
|
|
1204
|
+
/** Load progressive textures for the given material
|
|
1205
|
+
* @param material the material to load the textures for
|
|
1206
|
+
* @param level the LOD level to load. Level 0 is the best quality, higher levels are lower quality
|
|
1207
|
+
* @returns Promise with true if the LOD was loaded, false if not
|
|
1208
|
+
*/
|
|
1209
|
+
loadProgressiveTextures(t, e, s) {
|
|
1210
|
+
if (!t) return;
|
|
1211
|
+
if (Array.isArray(t)) {
|
|
1212
|
+
for (const o of t)
|
|
1213
|
+
this.loadProgressiveTextures(o, e);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
let r = !1;
|
|
1217
|
+
if ((t[U] === void 0 || e < t[U]) && (r = !0), s !== void 0 && s >= 0 && (r = t[U] != s, e = s), r) {
|
|
1218
|
+
t[U] = e;
|
|
1219
|
+
const o = m.assignTextureLOD(t, e).then((n) => {
|
|
1220
|
+
this._lodchangedlisteners.forEach((l) => l({ type: "texture", level: e, object: t }));
|
|
1221
|
+
});
|
|
1222
|
+
ce.addPromise("texture", t, o, this._newPromiseGroups);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
/** Load progressive meshes for the given mesh
|
|
1226
|
+
* @param mesh the mesh to load the LOD for
|
|
1227
|
+
* @param index the index of the mesh if it's part of a group
|
|
1228
|
+
* @param level the LOD level to load. Level 0 is the best quality, higher levels are lower quality
|
|
1229
|
+
* @returns Promise with true if the LOD was loaded, false if not
|
|
1230
|
+
*/
|
|
1231
|
+
loadProgressiveMeshes(t, e) {
|
|
1232
|
+
if (!t) return Promise.resolve(null);
|
|
1233
|
+
let s = t[U] !== e;
|
|
1234
|
+
const r = t["DEBUG:LOD"];
|
|
1235
|
+
if (r != null && (s = t[U] != r, e = r), s) {
|
|
1236
|
+
t[U] = e;
|
|
1237
|
+
const o = t.geometry, n = m.assignMeshLOD(t, e).then((l) => (l && t[U] == e && o != t.geometry && this._lodchangedlisteners.forEach((a) => a({ type: "mesh", level: e, object: t })), l));
|
|
1238
|
+
return ce.addPromise("mesh", t, n, this._newPromiseGroups), n;
|
|
1239
|
+
}
|
|
1240
|
+
return Promise.resolve(null);
|
|
1241
|
+
}
|
|
1242
|
+
// private testIfLODLevelsAreAvailable() {
|
|
1243
|
+
_sphere = new Ce();
|
|
1244
|
+
_tempBox = new ge();
|
|
1245
|
+
_tempBox2 = new ge();
|
|
1246
|
+
tempMatrix = new Me();
|
|
1247
|
+
_tempWorldPosition = new A();
|
|
1248
|
+
_tempBoxSize = new A();
|
|
1249
|
+
_tempBox2Size = new A();
|
|
1250
|
+
static corner0 = new A();
|
|
1251
|
+
static corner1 = new A();
|
|
1252
|
+
static corner2 = new A();
|
|
1253
|
+
static corner3 = new A();
|
|
1254
|
+
static _tempPtInside = new A();
|
|
1255
|
+
static isInside(t, e) {
|
|
1256
|
+
const s = t.min, r = t.max, o = (s.x + r.x) * 0.5, n = (s.y + r.y) * 0.5;
|
|
1257
|
+
return this._tempPtInside.set(o, n, s.z).applyMatrix4(e).z < 0;
|
|
1258
|
+
}
|
|
1259
|
+
static skinnedMeshBoundsFrameOffsetCounter = 0;
|
|
1260
|
+
static $skinnedMeshBoundsOffset = /* @__PURE__ */ Symbol("gltf-progressive-skinnedMeshBoundsOffset");
|
|
1261
|
+
// #region calculateLodLevel
|
|
1262
|
+
calculateLodLevel(t, e, s, r, o) {
|
|
1263
|
+
if (!e) {
|
|
1264
|
+
o.mesh_lod = -1, o.texture_lod = -1;
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if (!t) {
|
|
1268
|
+
o.mesh_lod = -1, o.texture_lod = -1;
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
let l = 10 + 1, a = !1;
|
|
1272
|
+
if (k && e["DEBUG:LOD"] != null)
|
|
1273
|
+
return e["DEBUG:LOD"];
|
|
1274
|
+
const u = m.getMeshLODExtension(e.geometry)?.lods, c = m.getPrimitiveIndex(e.geometry), y = u && u.length > 0, w = m.getMaterialMinMaxLODsCount(e.material), _ = w.min_count !== 1 / 0 && w.min_count >= 0 && w.max_count >= 0;
|
|
1275
|
+
if (!y && !_) {
|
|
1276
|
+
o.mesh_lod = 0, o.texture_lod = 0;
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
y || (a = !0, l = 0);
|
|
1280
|
+
const R = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
|
|
1281
|
+
let M = e.geometry.boundingBox;
|
|
1282
|
+
if (e.type === "SkinnedMesh") {
|
|
1283
|
+
const d = e;
|
|
1284
|
+
if (!d.boundingBox)
|
|
1285
|
+
d.computeBoundingBox();
|
|
1286
|
+
else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
|
|
1287
|
+
if (!d[v.$skinnedMeshBoundsOffset]) {
|
|
1288
|
+
const b = v.skinnedMeshBoundsFrameOffsetCounter++;
|
|
1289
|
+
d[v.$skinnedMeshBoundsOffset] = b;
|
|
1290
|
+
}
|
|
1291
|
+
const g = d[v.$skinnedMeshBoundsOffset];
|
|
1292
|
+
if ((s.frames + g) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
|
|
1293
|
+
const b = ee(d), B = d.geometry;
|
|
1294
|
+
b && (d.geometry = b), d.computeBoundingBox(), d.geometry = B;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
M = d.boundingBox;
|
|
1298
|
+
}
|
|
1299
|
+
if (M) {
|
|
1300
|
+
const d = t;
|
|
1301
|
+
if (e.geometry.attributes.color && e.geometry.attributes.color.count < 100 && e.geometry.boundingSphere) {
|
|
1302
|
+
this._sphere.copy(e.geometry.boundingSphere), this._sphere.applyMatrix4(e.matrixWorld);
|
|
1303
|
+
const h = t.getWorldPosition(this._tempWorldPosition);
|
|
1304
|
+
if (this._sphere.containsPoint(h)) {
|
|
1305
|
+
o.mesh_lod = 0, o.texture_lod = 0;
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (this._tempBox.copy(M), this._tempBox.applyMatrix4(e.matrixWorld), d.isPerspectiveCamera && v.isInside(this._tempBox, this.projectionScreenMatrix)) {
|
|
1310
|
+
o.mesh_lod = 0, o.texture_lod = 0;
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
if (this._tempBox.applyMatrix4(this.projectionScreenMatrix), this.renderer.xr.enabled && d.isPerspectiveCamera && d.fov > 70) {
|
|
1314
|
+
const h = this._tempBox.min, p = this._tempBox.max;
|
|
1315
|
+
let O = h.x, S = h.y, G = p.x, Y = p.y;
|
|
1316
|
+
const te = 2, ie = 1.5, se = (h.x + p.x) * 0.5, re = (h.y + p.y) * 0.5;
|
|
1317
|
+
O = (O - se) * te + se, S = (S - re) * te + re, G = (G - se) * te + se, Y = (Y - re) * te + re;
|
|
1318
|
+
const Fe = O < 0 && G > 0 ? 0 : Math.min(Math.abs(h.x), Math.abs(p.x)), We = S < 0 && Y > 0 ? 0 : Math.min(Math.abs(h.y), Math.abs(p.y)), ae = Math.max(Fe, We);
|
|
1319
|
+
s.lastCentrality = (ie - ae) * (ie - ae) * (ie - ae);
|
|
1320
|
+
} else
|
|
1321
|
+
s.lastCentrality = 1;
|
|
1322
|
+
const g = this._tempBox.getSize(this._tempBoxSize);
|
|
1323
|
+
g.multiplyScalar(0.5), screen.availHeight > 0 && R > 0 && g.multiplyScalar(R / screen.availHeight), t.isPerspectiveCamera ? g.x *= t.aspect : t.isOrthographicCamera;
|
|
1324
|
+
const b = t.matrixWorldInverse, B = this._tempBox2;
|
|
1325
|
+
B.copy(M), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(b);
|
|
1326
|
+
const D = B.getSize(this._tempBox2Size), q = Math.max(D.x, D.y);
|
|
1327
|
+
if (Math.max(g.x, g.y) != 0 && q != 0 && (g.z = D.z / Math.max(D.x, D.y) * Math.max(g.x, g.y)), s.lastScreenCoverage = Math.max(g.x, g.y, g.z), s.lastScreenspaceVolume.copy(g), s.lastScreenCoverage *= s.lastCentrality, k && v.debugDrawLine) {
|
|
1328
|
+
const h = this.tempMatrix.copy(this.projectionScreenMatrix);
|
|
1329
|
+
h.invert();
|
|
1330
|
+
const p = v.corner0, O = v.corner1, S = v.corner2, G = v.corner3;
|
|
1331
|
+
p.copy(this._tempBox.min), O.copy(this._tempBox.max), O.x = p.x, S.copy(this._tempBox.max), S.y = p.y, G.copy(this._tempBox.max);
|
|
1332
|
+
const Y = (p.z + G.z) * 0.5;
|
|
1333
|
+
p.z = O.z = S.z = G.z = Y, p.applyMatrix4(h), O.applyMatrix4(h), S.applyMatrix4(h), G.applyMatrix4(h), v.debugDrawLine(p, O, 255), v.debugDrawLine(p, S, 255), v.debugDrawLine(O, G, 255), v.debugDrawLine(S, G, 255);
|
|
1334
|
+
}
|
|
1335
|
+
let L = 999;
|
|
1336
|
+
if (u && s.lastScreenCoverage > 0)
|
|
1337
|
+
for (let h = 0; h < u.length; h++) {
|
|
1338
|
+
const p = u[h], S = (p.densities?.[c] || p.density || 1e-5) / s.lastScreenCoverage;
|
|
1339
|
+
if (c > 0 && Oe() && !p.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] && (window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] = !0, console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")), S < r) {
|
|
1340
|
+
L = h;
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
L < l && (l = L, a = !0);
|
|
1345
|
+
}
|
|
1346
|
+
if (a ? o.mesh_lod = l : o.mesh_lod = s.lastLodLevel_Mesh, k && o.mesh_lod != s.lastLodLevel_Mesh) {
|
|
1347
|
+
const g = u?.[o.mesh_lod];
|
|
1348
|
+
g && console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${g.densities?.[c].toFixed(0)}) | ${e.name}`);
|
|
1349
|
+
}
|
|
1350
|
+
if (_) {
|
|
1351
|
+
const d = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1352
|
+
if (s.lastLodLevel_Texture < 0) {
|
|
1353
|
+
if (o.texture_lod = w.max_count - 1, k) {
|
|
1354
|
+
const g = w.lods[w.max_count - 1];
|
|
1355
|
+
k && console.log(`First Texture LOD ${o.texture_lod} (${g.max_height}px) - ${e.name}`);
|
|
1356
|
+
}
|
|
1357
|
+
} else {
|
|
1358
|
+
const g = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
|
|
1359
|
+
let b = s.lastScreenCoverage * 4;
|
|
1360
|
+
this.context?.engine === "model-viewer" && (b *= 1.5);
|
|
1361
|
+
const D = R / window.devicePixelRatio * b;
|
|
1362
|
+
let q = !1;
|
|
1363
|
+
for (let T = w.lods.length - 1; T >= 0; T--) {
|
|
1364
|
+
const L = w.lods[T];
|
|
1365
|
+
if (!(d && L.max_height >= 2048) && !(ve() && L.max_height > 4096) && (L.max_height > D || !q && T === 0)) {
|
|
1366
|
+
if (q = !0, o.texture_lod = T, k && o.texture_lod < s.lastLodLevel_Texture) {
|
|
1367
|
+
const x = L.max_height;
|
|
1368
|
+
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${x}px
|
|
1369
|
+
Screensize: ${D.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${g.toFixed(1)}
|
|
1370
|
+
${e.name}`);
|
|
1371
|
+
}
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
} else
|
|
1377
|
+
o.texture_lod = 0;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
class Tt {
|
|
1381
|
+
frames = 0;
|
|
1382
|
+
lastLodLevel_Mesh = -1;
|
|
1383
|
+
lastLodLevel_Texture = -1;
|
|
1384
|
+
lastScreenCoverage = 0;
|
|
1385
|
+
lastScreenspaceVolume = new A();
|
|
1386
|
+
lastCentrality = 0;
|
|
1387
|
+
}
|
|
1388
|
+
const Se = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), oe = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
|
|
1389
|
+
let he = null;
|
|
1390
|
+
function Be() {
|
|
1391
|
+
const i = Pt();
|
|
1392
|
+
i && (i.mapURLs(function(t) {
|
|
1393
|
+
return Te(), t;
|
|
1394
|
+
}), Te(), he?.disconnect(), he = new MutationObserver((t) => {
|
|
1395
|
+
t.forEach((e) => {
|
|
1396
|
+
e.addedNodes.forEach((s) => {
|
|
1397
|
+
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ge(s);
|
|
1398
|
+
});
|
|
1399
|
+
});
|
|
1400
|
+
}), he.observe(document, { childList: !0, subtree: !0 }));
|
|
1401
|
+
}
|
|
1402
|
+
function Pt() {
|
|
1403
|
+
if (typeof customElements > "u") return null;
|
|
1404
|
+
const i = customElements.get("model-viewer");
|
|
1405
|
+
return i || (customElements.whenDefined("model-viewer").then(() => {
|
|
1406
|
+
console.debug("[gltf-progressive] model-viewer defined"), Be();
|
|
1407
|
+
}), null);
|
|
1408
|
+
}
|
|
1409
|
+
function Te() {
|
|
1410
|
+
if (typeof document > "u") return;
|
|
1411
|
+
document.querySelectorAll("model-viewer").forEach((t) => {
|
|
1412
|
+
Ge(t);
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
const Pe = /* @__PURE__ */ new WeakSet();
|
|
1416
|
+
let Ct = 0;
|
|
1417
|
+
function Ge(i) {
|
|
1418
|
+
if (!i || Pe.has(i))
|
|
1419
|
+
return null;
|
|
1420
|
+
Pe.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++Ct + `
|
|
1421
|
+
`, i.getAttribute("src"));
|
|
1422
|
+
let t = null, e = null, s = null;
|
|
1423
|
+
for (let r = i; r != null; r = Object.getPrototypeOf(r)) {
|
|
1424
|
+
const o = Object.getOwnPropertySymbols(r), n = o.find((u) => u.toString() == "Symbol(renderer)"), l = o.find((u) => u.toString() == "Symbol(scene)"), a = o.find((u) => u.toString() == "Symbol(needsRender)");
|
|
1425
|
+
!t && n != null && (t = i[n].threeRenderer), !e && l != null && (e = i[l]), !s && a != null && (s = i[a]);
|
|
1426
|
+
}
|
|
1427
|
+
if (t && e) {
|
|
1428
|
+
let r = function() {
|
|
1429
|
+
if (s) {
|
|
1430
|
+
let n = 0, l = setInterval(() => {
|
|
1431
|
+
if (n++ > 5) {
|
|
1432
|
+
clearInterval(l);
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
s?.call(i);
|
|
1436
|
+
}, 300);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
console.debug("[gltf-progressive] setup model-viewer");
|
|
1440
|
+
const o = v.get(t, { engine: "model-viewer" });
|
|
1441
|
+
return v.addPlugin(new At()), o.enable(), o.addEventListener("changed", () => {
|
|
1442
|
+
s?.call(i);
|
|
1443
|
+
}), i.addEventListener("model-visibility", (n) => {
|
|
1444
|
+
n.detail.visible && s?.call(i);
|
|
1445
|
+
}), i.addEventListener("load", () => {
|
|
1446
|
+
r();
|
|
1447
|
+
}), () => {
|
|
1448
|
+
o.disable();
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
class At {
|
|
1454
|
+
_didWarnAboutMissingUrl = !1;
|
|
1455
|
+
onBeforeUpdateLOD(t, e, s, r) {
|
|
1456
|
+
this.tryParseMeshLOD(e, r), this.tryParseTextureLOD(e, r);
|
|
1457
|
+
}
|
|
1458
|
+
getUrl(t) {
|
|
1459
|
+
if (!t)
|
|
1460
|
+
return null;
|
|
1461
|
+
let e = t.getAttribute("src");
|
|
1462
|
+
return e || (e = t.src), e || (this._didWarnAboutMissingUrl || console.warn("No url found in modelviewer", t), this._didWarnAboutMissingUrl = !0), e;
|
|
1463
|
+
}
|
|
1464
|
+
tryGetCurrentGLTF(t) {
|
|
1465
|
+
return t._currentGLTF;
|
|
1466
|
+
}
|
|
1467
|
+
tryGetCurrentModelViewer(t) {
|
|
1468
|
+
return t.element;
|
|
1469
|
+
}
|
|
1470
|
+
tryParseTextureLOD(t, e) {
|
|
1471
|
+
if (e[oe] == !0) return;
|
|
1472
|
+
e[oe] = !0;
|
|
1473
|
+
const s = this.tryGetCurrentGLTF(t), r = this.tryGetCurrentModelViewer(t), o = this.getUrl(r);
|
|
1474
|
+
if (o && s && e.material) {
|
|
1475
|
+
let n = function(a) {
|
|
1476
|
+
if (a[oe] == !0) return;
|
|
1477
|
+
a[oe] = !0, a.userData && (a.userData.LOD = -1);
|
|
1478
|
+
const u = Object.keys(a);
|
|
1479
|
+
for (let c = 0; c < u.length; c++) {
|
|
1480
|
+
const y = u[c], w = a[y];
|
|
1481
|
+
if (w?.isTexture === !0) {
|
|
1482
|
+
const _ = w.userData?.associations?.textures;
|
|
1483
|
+
if (_ == null) continue;
|
|
1484
|
+
const R = s.parser.json.textures[_];
|
|
1485
|
+
if (!R) {
|
|
1486
|
+
console.warn("Texture data not found for texture index " + _);
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
if (R?.extensions?.[W]) {
|
|
1490
|
+
const M = R.extensions[W];
|
|
1491
|
+
M && o && m.registerTexture(o, w, M.lods.length, _, M);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
const l = e.material;
|
|
1497
|
+
if (Array.isArray(l)) for (const a of l) n(a);
|
|
1498
|
+
else n(l);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
tryParseMeshLOD(t, e) {
|
|
1502
|
+
if (e[Se] == !0) return;
|
|
1503
|
+
e[Se] = !0;
|
|
1504
|
+
const s = this.tryGetCurrentModelViewer(t), r = this.getUrl(s);
|
|
1505
|
+
if (!r)
|
|
1506
|
+
return;
|
|
1507
|
+
const o = e.userData?.gltfExtensions?.[W];
|
|
1508
|
+
if (o && r) {
|
|
1509
|
+
const n = e.uuid;
|
|
1510
|
+
m.registerMesh(r, n, e, 0, o.lods.length, o);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
function Rt(...i) {
|
|
1515
|
+
let t, e, s, r;
|
|
1516
|
+
switch (i.length) {
|
|
1517
|
+
case 2:
|
|
1518
|
+
[s, e] = i, r = {};
|
|
1519
|
+
break;
|
|
1520
|
+
case 3:
|
|
1521
|
+
[s, e, r] = i;
|
|
1522
|
+
break;
|
|
1523
|
+
case 4:
|
|
1524
|
+
[t, e, s, r] = i;
|
|
1525
|
+
break;
|
|
1526
|
+
default:
|
|
1527
|
+
throw new Error("Invalid arguments");
|
|
1528
|
+
}
|
|
1529
|
+
Le(e), Re(s), Ie(s, {
|
|
1530
|
+
progressive: !0,
|
|
1531
|
+
...r?.hints
|
|
1532
|
+
}), s.register((n) => new m(n));
|
|
1533
|
+
const o = v.get(e);
|
|
1534
|
+
return r?.enableLODsManager !== !1 && o.enable(), o;
|
|
1535
|
+
}
|
|
1536
|
+
Be();
|
|
1537
|
+
if (!yt) {
|
|
1538
|
+
const i = {
|
|
1539
|
+
gltfProgressive: {
|
|
1540
|
+
useNeedleProgressive: Rt,
|
|
1541
|
+
LODsManager: v,
|
|
1542
|
+
configureLoader: Ie,
|
|
1543
|
+
getRaycastMesh: ee,
|
|
1544
|
+
useRaycastMeshes: wt
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
if (!globalThis.Needle)
|
|
1548
|
+
globalThis.Needle = i;
|
|
1549
|
+
else
|
|
1550
|
+
for (const t in i)
|
|
1551
|
+
globalThis.Needle[t] = i[t];
|
|
1552
|
+
}
|
|
1553
|
+
export {
|
|
1554
|
+
W as EXTENSION_NAME,
|
|
1555
|
+
v as LODsManager,
|
|
1556
|
+
m as NEEDLE_progressive,
|
|
1557
|
+
ot as VERSION,
|
|
1558
|
+
Re as addDracoAndKTX2Loaders,
|
|
1559
|
+
Ie as configureLoader,
|
|
1560
|
+
Le as createLoaders,
|
|
1561
|
+
ee as getRaycastMesh,
|
|
1562
|
+
Be as patchModelViewer,
|
|
1563
|
+
xt as registerRaycastMesh,
|
|
1564
|
+
lt as setDracoDecoderLocation,
|
|
1565
|
+
ut as setKTX2TranscoderLocation,
|
|
1566
|
+
Rt as useNeedleProgressive,
|
|
1567
|
+
wt as useRaycastMeshes
|
|
1568
|
+
};
|