@needle-tools/gltf-progressive 4.0.0-alpha.1 → 4.0.0-alpha.3
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/CHANGELOG.md +26 -2
- package/LICENSE +21 -0
- package/README.md +47 -11
- package/examples/modelviewer-multiple.html +4 -4
- package/examples/modelviewer.html +4 -4
- package/examples/offscreen/index.html +15 -0
- package/examples/offscreen/main.js +98 -0
- package/examples/react-three-fiber/index.html +2 -2
- package/examples/react-three-fiber/package-lock.json +482 -484
- package/examples/react-three-fiber/package.json +4 -4
- package/examples/react-three-fiber/src/App.tsx +76 -21
- package/examples/react-three-fiber/src/styles.css +2 -4
- package/examples/react-three-fiber/vite.config.js +2 -2
- package/examples/shared/example-utils.js +297 -0
- package/examples/shared/example.css +44 -0
- package/examples/shared/runtime.js +34 -0
- package/examples/threejs/index.html +6 -10
- package/examples/threejs/main.js +5 -23
- package/examples/webgpu/index.html +15 -0
- package/examples/webgpu/main.js +105 -0
- package/examples/worker-rendering/index.html +15 -0
- package/examples/worker-rendering/main.js +109 -0
- package/examples/worker-rendering/worker.js +166 -0
- package/gltf-progressive.js +1016 -647
- package/gltf-progressive.min.js +9 -9
- package/gltf-progressive.umd.cjs +9 -9
- package/lib/extension.d.ts +75 -7
- package/lib/extension.js +379 -111
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/loaders.js +17 -3
- package/lib/lods.debug.js +2 -2
- package/lib/lods.manager.d.ts +93 -15
- package/lib/lods.manager.js +331 -157
- package/lib/lods.promise.js +1 -1
- package/lib/plugins/modelviewer.js +1 -1
- package/lib/utils.internal.js +20 -6
- package/lib/version.js +1 -1
- package/lib/worker/loader.mainthread.js +4 -1
- package/package.json +10 -3
package/gltf-progressive.js
CHANGED
|
@@ -1,87 +1,99 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import * as Xe from "three";
|
|
2
|
+
import { RedFormat as je, RedIntegerFormat as Ke, RGFormat as He, RGIntegerFormat as Ye, RGBFormat as Qe, RGBAFormat as Je, RGBAIntegerFormat as Ze, BufferGeometry as H, Mesh as Y, Box3 as fe, Vector3 as A, Sphere as Ie, CompressedTexture as et, Texture as W, Matrix3 as tt, InterleavedBuffer as st, InterleavedBufferAttribute as rt, BufferAttribute as nt, TextureLoader as it, Color as $e, Matrix4 as Ge } from "three";
|
|
3
|
+
import { GLTFLoader as Me } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
|
+
import { MeshoptDecoder as ot } from "three/examples/jsm/libs/meshopt_decoder.module.js";
|
|
5
|
+
import { DRACOLoader as at } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
6
|
+
import { KTX2Loader as lt } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
7
|
+
const ut = "";
|
|
8
|
+
globalThis.GLTF_PROGRESSIVE_VERSION = ut;
|
|
8
9
|
console.debug("[gltf-progressive] version -");
|
|
9
|
-
let
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
fetch(
|
|
10
|
+
let B = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", Q = "https://cdn.needle.tools/static/three/0.179.1/basis2/";
|
|
11
|
+
const ct = B, ft = Q, Be = new URL(B + "draco_decoder.js");
|
|
12
|
+
Be.searchParams.append("range", "true");
|
|
13
|
+
fetch(Be, {
|
|
13
14
|
method: "GET",
|
|
14
15
|
headers: {
|
|
15
16
|
Range: "bytes=0-1"
|
|
16
17
|
}
|
|
17
18
|
}).catch((i) => {
|
|
18
|
-
console.debug(`Failed to fetch remote Draco decoder from ${
|
|
19
|
+
console.debug(`Failed to fetch remote Draco decoder from ${B} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), B === ct && ht("./include/draco/"), Q === ft && gt("./include/ktx2/");
|
|
19
20
|
}).finally(() => {
|
|
20
|
-
|
|
21
|
+
We();
|
|
21
22
|
});
|
|
22
|
-
const
|
|
23
|
-
dracoDecoderPath:
|
|
24
|
-
ktx2TranscoderPath:
|
|
23
|
+
const dt = () => ({
|
|
24
|
+
dracoDecoderPath: B,
|
|
25
|
+
ktx2TranscoderPath: Q
|
|
25
26
|
});
|
|
26
|
-
function
|
|
27
|
-
|
|
27
|
+
function ht(i) {
|
|
28
|
+
B = i, k && k[xe] != B ? (console.debug("Updating Draco decoder path to " + i), k[xe] = B, k.setDecoderPath(B), k.preload()) : console.debug("Setting Draco decoder path to " + i);
|
|
28
29
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
30
|
+
function gt(i) {
|
|
31
|
+
Q = i, q && q.transcoderPath != Q ? (console.debug("Updating KTX2 transcoder path to " + i), q.setTranscoderPath(Q), q.init()) : console.debug("Setting KTX2 transcoder path to " + i);
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
return
|
|
33
|
+
function _e(i) {
|
|
34
|
+
return We(), i ? q.detectSupport(i) : i !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: k, ktx2Loader: q, meshoptDecoder: ce };
|
|
34
35
|
}
|
|
35
|
-
function
|
|
36
|
-
i.dracoLoader || i.setDRACOLoader(
|
|
36
|
+
function qe(i) {
|
|
37
|
+
i.dracoLoader || i.setDRACOLoader(k), i.ktx2Loader || i.setKTX2Loader(q), i.meshoptDecoder || i.setMeshoptDecoder(ce);
|
|
37
38
|
}
|
|
38
|
-
const
|
|
39
|
-
let
|
|
40
|
-
function
|
|
41
|
-
|
|
39
|
+
const xe = /* @__PURE__ */ Symbol("dracoDecoderPath");
|
|
40
|
+
let k, ce, q;
|
|
41
|
+
function We() {
|
|
42
|
+
k || (k = new at(), k[xe] = B, k.setDecoderPath(B), k.preload()), q || (q = new lt(), q.setTranscoderPath(Q), q.init()), ce || (ce = ot);
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
-
function
|
|
45
|
-
let e =
|
|
46
|
-
e ? e = Object.assign(e, t) : e = t,
|
|
44
|
+
const we = /* @__PURE__ */ new WeakMap();
|
|
45
|
+
function Fe(i, t) {
|
|
46
|
+
let e = we.get(i);
|
|
47
|
+
e ? e = Object.assign(e, t) : e = t, we.set(i, e);
|
|
47
48
|
}
|
|
48
|
-
const
|
|
49
|
-
function
|
|
50
|
-
const t =
|
|
49
|
+
const pt = Me.prototype.load;
|
|
50
|
+
function mt(...i) {
|
|
51
|
+
const t = we.get(this);
|
|
51
52
|
let e = i[0];
|
|
52
|
-
const s =
|
|
53
|
-
if (s
|
|
54
|
-
const
|
|
55
|
-
|
|
53
|
+
const s = yt(e);
|
|
54
|
+
if (s?.hostname.endsWith("needle.tools")) {
|
|
55
|
+
const n = t?.progressive !== void 0 ? t.progressive : !0, o = t?.usecase ? t.usecase : "default";
|
|
56
|
+
n ? this.requestHeader.Accept = `*/*;progressive=allowed;usecase=${o}` : this.requestHeader.Accept = `*/*;usecase=${o}`, e = s.toString();
|
|
57
|
+
}
|
|
58
|
+
return i[0] = e, pt?.call(this, ...i);
|
|
59
|
+
}
|
|
60
|
+
Me.prototype.load = mt;
|
|
61
|
+
function yt(i) {
|
|
62
|
+
try {
|
|
63
|
+
if (i instanceof URL)
|
|
64
|
+
return i;
|
|
65
|
+
const t = globalThis.location?.href;
|
|
66
|
+
return t ? new URL(i, t) : new URL(i);
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
56
69
|
}
|
|
57
|
-
return i[0] = e, ct?.call(this, ...i);
|
|
58
70
|
}
|
|
59
|
-
|
|
60
|
-
function De(i) {
|
|
71
|
+
function Oe(i) {
|
|
61
72
|
return i != null && i.data != null;
|
|
62
73
|
}
|
|
63
|
-
function
|
|
74
|
+
function Le(i) {
|
|
64
75
|
const t = i.source?.data;
|
|
65
76
|
return t != null && typeof t == "object" ? t : null;
|
|
66
77
|
}
|
|
67
|
-
function
|
|
78
|
+
function xt(i) {
|
|
68
79
|
const t = i.image;
|
|
69
80
|
return t != null && typeof t == "object" ? t : null;
|
|
70
81
|
}
|
|
71
|
-
function
|
|
72
|
-
const t =
|
|
82
|
+
function ve(i) {
|
|
83
|
+
const t = xt(i), e = Le(i);
|
|
73
84
|
return {
|
|
74
85
|
width: t?.width || e?.width || 0,
|
|
75
86
|
height: t?.height || e?.height || 0
|
|
76
87
|
};
|
|
77
88
|
}
|
|
78
|
-
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
X("debugprogressive");
|
|
90
|
+
function X(i) {
|
|
91
|
+
const t = globalThis.location?.href;
|
|
92
|
+
if (!t) return !1;
|
|
93
|
+
const s = new URL(t).searchParams.get(i);
|
|
94
|
+
return s == null || s === "0" || s === "false" ? !1 : s === "" ? !0 : s;
|
|
83
95
|
}
|
|
84
|
-
function
|
|
96
|
+
function wt(i, t) {
|
|
85
97
|
if (t === void 0 || i === void 0 || t.startsWith("./") || t.startsWith("http") || t.startsWith("data:") || t.startsWith("blob:"))
|
|
86
98
|
return t;
|
|
87
99
|
const e = i.lastIndexOf("/");
|
|
@@ -92,18 +104,21 @@ function ht(i, t) {
|
|
|
92
104
|
}
|
|
93
105
|
return t;
|
|
94
106
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
107
|
+
function be() {
|
|
108
|
+
if (se !== void 0) return se;
|
|
109
|
+
const i = globalThis.navigator?.userAgent || "";
|
|
110
|
+
return se = /iPhone|iPad|iPod|Android|IEMobile/i.test(i), X("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", se), se;
|
|
97
111
|
}
|
|
98
|
-
let
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
let se;
|
|
113
|
+
function Ue() {
|
|
114
|
+
const i = globalThis.location?.href;
|
|
115
|
+
if (!i) return !1;
|
|
116
|
+
const t = new URL(i), e = t.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);
|
|
117
|
+
return t.hostname === "127.0.0.1" || e;
|
|
103
118
|
}
|
|
104
|
-
class
|
|
119
|
+
class Lt {
|
|
105
120
|
constructor(t, e = {}) {
|
|
106
|
-
this.maxConcurrent = t, this.debug = e.debug ?? !1,
|
|
121
|
+
this.maxConcurrent = t, this.debug = e.debug ?? !1, typeof globalThis.requestAnimationFrame == "function" ? globalThis.requestAnimationFrame(this.tick) : setTimeout(this.tick, 0);
|
|
107
122
|
}
|
|
108
123
|
_running = /* @__PURE__ */ new Map();
|
|
109
124
|
_queue = [];
|
|
@@ -130,97 +145,98 @@ class gt {
|
|
|
130
145
|
this.debug && console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);
|
|
131
146
|
const { key: s, resolve: r } = this._queue.shift();
|
|
132
147
|
r({
|
|
133
|
-
use: (
|
|
148
|
+
use: (n) => this.add(s, n)
|
|
134
149
|
});
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
}
|
|
138
|
-
function
|
|
139
|
-
const t = i.image, e = t?.width ?? 0, s = t?.height ?? 0, r = t?.depth ?? 1,
|
|
140
|
-
return e * s * r *
|
|
153
|
+
function vt(i) {
|
|
154
|
+
const t = i.image, e = t?.width ?? 0, s = t?.height ?? 0, r = t?.depth ?? 1, n = Math.floor(Math.log2(Math.max(e, s, r))) + 1, o = Dt(i);
|
|
155
|
+
return e * s * r * o * (1 - Math.pow(0.25, n)) / (1 - 0.25);
|
|
141
156
|
}
|
|
142
|
-
function
|
|
157
|
+
function Dt(i) {
|
|
143
158
|
let t = 4;
|
|
144
159
|
const e = i.format;
|
|
145
|
-
e ===
|
|
160
|
+
e === je || e === Ke ? t = 1 : e === He || e === Ye ? t = 2 : e === Qe || e === 1029 ? t = 3 : (e === Je || e === Ze) && (t = 4);
|
|
146
161
|
let s = 1;
|
|
147
162
|
const r = i.type;
|
|
148
163
|
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
164
|
}
|
|
150
|
-
const
|
|
151
|
-
function
|
|
152
|
-
return i?.[
|
|
165
|
+
const Mt = typeof window > "u" && typeof document > "u", De = /* @__PURE__ */ Symbol("needle:raycast-mesh");
|
|
166
|
+
function ae(i) {
|
|
167
|
+
return i?.[De] instanceof H ? i[De] : null;
|
|
153
168
|
}
|
|
154
|
-
function
|
|
155
|
-
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !
|
|
156
|
-
const s =
|
|
157
|
-
s.userData = { isRaycastMesh: !0 }, i[
|
|
169
|
+
function _t(i, t) {
|
|
170
|
+
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !ae(i)) {
|
|
171
|
+
const s = Tt(t);
|
|
172
|
+
s.userData = { isRaycastMesh: !0 }, i[De] = s;
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
|
-
function
|
|
175
|
+
function bt(i = !0) {
|
|
161
176
|
if (i) {
|
|
162
|
-
if (
|
|
163
|
-
const t =
|
|
164
|
-
|
|
165
|
-
const r = this,
|
|
166
|
-
let
|
|
167
|
-
|
|
177
|
+
if (re) return;
|
|
178
|
+
const t = re = Y.prototype.raycast;
|
|
179
|
+
Y.prototype.raycast = function(e, s) {
|
|
180
|
+
const r = this, n = ae(r);
|
|
181
|
+
let o;
|
|
182
|
+
n && r.isMesh && (o = r.geometry, r.geometry = n), t.call(this, e, s), o && (r.geometry = o);
|
|
168
183
|
};
|
|
169
184
|
} else {
|
|
170
|
-
if (!
|
|
171
|
-
|
|
185
|
+
if (!re) return;
|
|
186
|
+
Y.prototype.raycast = re, re = null;
|
|
172
187
|
}
|
|
173
188
|
}
|
|
174
|
-
let
|
|
175
|
-
function
|
|
176
|
-
const t = new
|
|
189
|
+
let re = null;
|
|
190
|
+
function Tt(i) {
|
|
191
|
+
const t = new H();
|
|
177
192
|
for (const e in i.attributes)
|
|
178
193
|
t.setAttribute(e, i.getAttribute(e));
|
|
179
194
|
return t.setIndex(i.getIndex()), t;
|
|
180
195
|
}
|
|
181
|
-
const
|
|
182
|
-
let
|
|
183
|
-
if (
|
|
196
|
+
const U = new Array(), g = X("debugprogressive");
|
|
197
|
+
let oe, K = -1;
|
|
198
|
+
if (g && typeof window < "u") {
|
|
184
199
|
let i = function() {
|
|
185
|
-
|
|
186
|
-
}
|
|
200
|
+
K += 1, K >= t && (K = -1), console.log(`Toggle LOD level [${K}]`);
|
|
201
|
+
};
|
|
202
|
+
const t = 6;
|
|
187
203
|
window.addEventListener("keyup", (e) => {
|
|
188
|
-
e.key === "p" && i(), e.key === "w" && (
|
|
204
|
+
e.key === "p" && i(), e.key === "w" && (oe = !oe, console.log(`Toggle wireframe [${oe}]`));
|
|
189
205
|
const s = parseInt(e.key);
|
|
190
|
-
!isNaN(s) && s >= 0 && (
|
|
206
|
+
!isNaN(s) && s >= 0 && (K = s, console.log(`Set LOD level to [${K}]`));
|
|
191
207
|
});
|
|
192
208
|
}
|
|
193
|
-
function
|
|
194
|
-
if (
|
|
209
|
+
function ze(i) {
|
|
210
|
+
if (g && oe !== void 0)
|
|
195
211
|
if (Array.isArray(i))
|
|
196
212
|
for (const t of i)
|
|
197
|
-
|
|
198
|
-
else i && "wireframe" in i && (i.wireframe =
|
|
213
|
+
ze(t);
|
|
214
|
+
else i && "wireframe" in i && (i.wireframe = oe === !0);
|
|
199
215
|
}
|
|
200
|
-
const
|
|
201
|
-
let
|
|
202
|
-
const
|
|
203
|
-
function
|
|
204
|
-
if (
|
|
205
|
-
const s =
|
|
206
|
-
|
|
207
|
-
const r =
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
const t =
|
|
211
|
-
return
|
|
216
|
+
const ne = new Array();
|
|
217
|
+
let Ot = 0;
|
|
218
|
+
const St = be() ? 2 : 10;
|
|
219
|
+
function Rt(i) {
|
|
220
|
+
if (ne.length < St) {
|
|
221
|
+
const s = ne.length;
|
|
222
|
+
g && console.warn(`[Worker] Creating new worker #${s}`);
|
|
223
|
+
const r = Te.createWorker(i || {});
|
|
224
|
+
return ne.push(r), r;
|
|
225
|
+
}
|
|
226
|
+
const t = Ot++ % ne.length;
|
|
227
|
+
return ne[t];
|
|
212
228
|
}
|
|
213
|
-
class
|
|
229
|
+
class Te {
|
|
214
230
|
constructor(t, e) {
|
|
215
231
|
this.worker = t, this._debug = e.debug ?? !1, t.onmessage = (s) => {
|
|
216
232
|
const r = s.data;
|
|
217
233
|
switch (this._debug && console.log("[Worker] EVENT", r), r.type) {
|
|
218
234
|
case "loaded-gltf":
|
|
219
|
-
for (const
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
235
|
+
for (const n of this._running)
|
|
236
|
+
if (n.url === r.result.url) {
|
|
237
|
+
Pt(r.result), n.resolve(r.result);
|
|
238
|
+
const o = n.url;
|
|
239
|
+
o.startsWith("blob:") && URL.revokeObjectURL(o);
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
}, t.onerror = (s) => {
|
|
@@ -232,24 +248,31 @@ class be {
|
|
|
232
248
|
static async createWorker(t) {
|
|
233
249
|
const e = new Worker(new URL(
|
|
234
250
|
/* @vite-ignore */
|
|
235
|
-
"/assets/gltf-progressive.worker-
|
|
251
|
+
"/assets/gltf-progressive.worker-Cj5rV8iI.js",
|
|
236
252
|
import.meta.url
|
|
237
253
|
), {
|
|
238
254
|
type: "module"
|
|
239
255
|
});
|
|
240
|
-
return new
|
|
256
|
+
return new Te(e, t);
|
|
241
257
|
}
|
|
242
258
|
_running = [];
|
|
243
259
|
_webglRenderer = null;
|
|
244
260
|
async load(t, e) {
|
|
245
|
-
const s =
|
|
261
|
+
const s = dt();
|
|
246
262
|
let r = e?.renderer;
|
|
247
263
|
r || (this._webglRenderer ??= (async () => {
|
|
248
264
|
const { WebGLRenderer: u } = await import("three");
|
|
249
265
|
return new u();
|
|
250
266
|
})(), r = await this._webglRenderer);
|
|
251
|
-
const l =
|
|
252
|
-
|
|
267
|
+
const l = _e(r).ktx2Loader.workerConfig;
|
|
268
|
+
if (t instanceof URL)
|
|
269
|
+
t = t.toString();
|
|
270
|
+
else if (t.startsWith("file:"))
|
|
271
|
+
t = URL.createObjectURL(new Blob([t]));
|
|
272
|
+
else if (!t.startsWith("blob:") && !t.startsWith("http:") && !t.startsWith("https:")) {
|
|
273
|
+
const u = globalThis.location?.href;
|
|
274
|
+
u && (t = new URL(t, u).toString());
|
|
275
|
+
}
|
|
253
276
|
const a = {
|
|
254
277
|
type: "load",
|
|
255
278
|
url: t,
|
|
@@ -266,23 +289,23 @@ class be {
|
|
|
266
289
|
}
|
|
267
290
|
_debug = !1;
|
|
268
291
|
}
|
|
269
|
-
function
|
|
292
|
+
function Pt(i) {
|
|
270
293
|
for (const t of i.geometries) {
|
|
271
|
-
const e = t.geometry, s = new
|
|
294
|
+
const e = t.geometry, s = new H();
|
|
272
295
|
if (s.name = e.name || "", e.index) {
|
|
273
296
|
const r = e.index;
|
|
274
|
-
s.setIndex(
|
|
297
|
+
s.setIndex(de(r));
|
|
275
298
|
}
|
|
276
299
|
for (const r in e.attributes) {
|
|
277
|
-
const
|
|
278
|
-
s.setAttribute(r,
|
|
300
|
+
const n = e.attributes[r], o = de(n);
|
|
301
|
+
s.setAttribute(r, o);
|
|
279
302
|
}
|
|
280
303
|
if (e.morphAttributes)
|
|
281
304
|
for (const r in e.morphAttributes) {
|
|
282
|
-
const
|
|
283
|
-
s.morphAttributes[r] =
|
|
305
|
+
const o = e.morphAttributes[r].map((l) => de(l));
|
|
306
|
+
s.morphAttributes[r] = o;
|
|
284
307
|
}
|
|
285
|
-
if (s.morphTargetsRelative = e.morphTargetsRelative ?? !1, s.boundingBox = new
|
|
308
|
+
if (s.morphTargetsRelative = e.morphTargetsRelative ?? !1, s.boundingBox = new fe(), s.boundingBox.min = new A(
|
|
286
309
|
e.boundingBox?.min.x,
|
|
287
310
|
e.boundingBox?.min.y,
|
|
288
311
|
e.boundingBox?.min.z
|
|
@@ -290,7 +313,7 @@ function Mt(i) {
|
|
|
290
313
|
e.boundingBox?.max.x,
|
|
291
314
|
e.boundingBox?.max.y,
|
|
292
315
|
e.boundingBox?.max.z
|
|
293
|
-
), s.boundingSphere = new
|
|
316
|
+
), s.boundingSphere = new Ie(
|
|
294
317
|
new A(
|
|
295
318
|
e.boundingSphere?.center.x,
|
|
296
319
|
e.boundingSphere?.center.y,
|
|
@@ -306,11 +329,11 @@ function Mt(i) {
|
|
|
306
329
|
const e = t.texture;
|
|
307
330
|
let s = null;
|
|
308
331
|
if (e.isCompressedTexture) {
|
|
309
|
-
const r = e.mipmaps, { width:
|
|
310
|
-
s = new
|
|
332
|
+
const r = e.mipmaps, { width: n, height: o } = ve(e);
|
|
333
|
+
s = new et(
|
|
311
334
|
r,
|
|
312
|
-
o,
|
|
313
335
|
n,
|
|
336
|
+
o,
|
|
314
337
|
e.format,
|
|
315
338
|
e.type,
|
|
316
339
|
e.mapping,
|
|
@@ -322,7 +345,7 @@ function Mt(i) {
|
|
|
322
345
|
e.colorSpace
|
|
323
346
|
);
|
|
324
347
|
} else
|
|
325
|
-
s = new
|
|
348
|
+
s = new W(
|
|
326
349
|
e.image,
|
|
327
350
|
e.mapping,
|
|
328
351
|
e.wrapS,
|
|
@@ -333,7 +356,7 @@ function Mt(i) {
|
|
|
333
356
|
e.type,
|
|
334
357
|
e.anisotropy,
|
|
335
358
|
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
|
|
359
|
+
), 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 tt(...e.matrix.elements);
|
|
337
360
|
if (!s) {
|
|
338
361
|
console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");
|
|
339
362
|
continue;
|
|
@@ -342,34 +365,57 @@ function Mt(i) {
|
|
|
342
365
|
}
|
|
343
366
|
return i;
|
|
344
367
|
}
|
|
345
|
-
function
|
|
368
|
+
function de(i) {
|
|
346
369
|
let t = i;
|
|
347
370
|
if ("isInterleavedBufferAttribute" in i && i.isInterleavedBufferAttribute) {
|
|
348
|
-
const e = i.data, s = e.array, r = new
|
|
349
|
-
t = new
|
|
350
|
-
} else "isBufferAttribute" in i && i.isBufferAttribute && (t = new
|
|
371
|
+
const e = i.data, s = e.array, r = new st(s, e.stride);
|
|
372
|
+
t = new rt(r, i.itemSize, s.byteOffset, i.normalized), t.offset = i.offset;
|
|
373
|
+
} else "isBufferAttribute" in i && i.isBufferAttribute && (t = new nt(i.array, i.itemSize, i.normalized), t.usage = i.usage, t.gpuType = i.gpuType, t.updateRanges = i.updateRanges);
|
|
351
374
|
return t;
|
|
352
375
|
}
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
class
|
|
376
|
+
const Ct = X("gltf-progressive-worker");
|
|
377
|
+
X("gltf-progressive-reduce-mipmaps");
|
|
378
|
+
const ie = X("gltf-progressive-gc"), he = /* @__PURE__ */ Symbol("needle-progressive-texture"), z = "NEEDLE_progressive";
|
|
379
|
+
class p {
|
|
357
380
|
/** The name of the extension */
|
|
358
381
|
get name() {
|
|
359
|
-
return
|
|
382
|
+
return z;
|
|
360
383
|
}
|
|
361
384
|
// #region PUBLIC API
|
|
385
|
+
/**
|
|
386
|
+
* Get the progressive mesh LOD extension data associated with a geometry.
|
|
387
|
+
* Returns the extension metadata (available LOD levels, vertex/index counts, densities) if the geometry was registered with progressive LODs, or `null` otherwise.
|
|
388
|
+
*
|
|
389
|
+
* @param geo - The buffer geometry to look up.
|
|
390
|
+
* @returns The mesh LOD extension data, or `null` if no progressive LODs are registered for this geometry.
|
|
391
|
+
*/
|
|
362
392
|
static getMeshLODExtension(t) {
|
|
363
393
|
const e = this.getAssignedLODInformation(t);
|
|
364
394
|
return e?.key ? this.lodInfos.get(e.key) : null;
|
|
365
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Get the glTF primitive index for a geometry within its parent mesh.
|
|
398
|
+
* A single glTF mesh node can contain multiple primitives (sub-geometries). This returns which primitive the geometry corresponds to.
|
|
399
|
+
*
|
|
400
|
+
* @param geo - The buffer geometry to look up.
|
|
401
|
+
* @returns The zero-based primitive index, or `-1` if no LOD information is assigned to this geometry.
|
|
402
|
+
*/
|
|
366
403
|
static getPrimitiveIndex(t) {
|
|
367
404
|
const e = this.getAssignedLODInformation(t)?.index;
|
|
368
405
|
return e ?? -1;
|
|
369
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Compute the minimum and maximum number of texture LOD levels across all textures of a material (or array of materials).
|
|
409
|
+
* Iterates over all texture slots on the material and collects LOD count ranges and per-level resolution bounds.
|
|
410
|
+
* Results are cached on the material so subsequent calls are free.
|
|
411
|
+
*
|
|
412
|
+
* @param material - A single material or an array of materials to inspect.
|
|
413
|
+
* @param minmax - Optional accumulator to merge results into (used internally for recursive calls with material arrays).
|
|
414
|
+
* @returns An object with `min_count` / `max_count` (the range of LOD levels across all textures) and a per-level `lods` array with `min_height` / `max_height`.
|
|
415
|
+
*/
|
|
370
416
|
static getMaterialMinMaxLODsCount(t, e) {
|
|
371
|
-
const s = this, r = "LODS:minmax",
|
|
372
|
-
if (
|
|
417
|
+
const s = this, r = "LODS:minmax", n = t[r];
|
|
418
|
+
if (n != null) return n;
|
|
373
419
|
if (e || (e = {
|
|
374
420
|
min_count: 1 / 0,
|
|
375
421
|
max_count: 0,
|
|
@@ -379,29 +425,29 @@ class m {
|
|
|
379
425
|
this.getMaterialMinMaxLODsCount(l, e);
|
|
380
426
|
return t[r] = e, e;
|
|
381
427
|
}
|
|
382
|
-
if (
|
|
428
|
+
if (g === "verbose" && console.log("getMaterialMinMaxLODsCount", t), t.type === "ShaderMaterial" || t.type === "RawShaderMaterial") {
|
|
383
429
|
const l = t;
|
|
384
430
|
for (const a of Object.keys(l.uniforms)) {
|
|
385
431
|
const u = l.uniforms[a].value;
|
|
386
|
-
u?.isTexture === !0 &&
|
|
432
|
+
u?.isTexture === !0 && o(u, e);
|
|
387
433
|
}
|
|
388
434
|
} else if (t.isMaterial)
|
|
389
435
|
for (const l of Object.keys(t)) {
|
|
390
436
|
const a = t[l];
|
|
391
|
-
a?.isTexture === !0 &&
|
|
437
|
+
a?.isTexture === !0 && o(a, e);
|
|
392
438
|
}
|
|
393
439
|
else
|
|
394
|
-
|
|
440
|
+
g && console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);
|
|
395
441
|
return t[r] = e, e;
|
|
396
|
-
function
|
|
442
|
+
function o(l, a) {
|
|
397
443
|
const u = s.getAssignedLODInformation(l);
|
|
398
444
|
if (u) {
|
|
399
|
-
const
|
|
400
|
-
if (
|
|
401
|
-
a.min_count = Math.min(a.min_count,
|
|
402
|
-
for (let
|
|
403
|
-
const
|
|
404
|
-
|
|
445
|
+
const d = s.lodInfos.get(u.key);
|
|
446
|
+
if (d && d.lods) {
|
|
447
|
+
a.min_count = Math.min(a.min_count, d.lods.length), a.max_count = Math.max(a.max_count, d.lods.length);
|
|
448
|
+
for (let c = 0; c < d.lods.length; c++) {
|
|
449
|
+
const f = d.lods[c];
|
|
450
|
+
f.width && (a.lods[c] = a.lods[c] || { min_height: 1 / 0, max_height: 0 }, a.lods[c].min_height = Math.min(a.lods[c].min_height, f.height), a.lods[c].max_height = Math.max(a.lods[c].max_height, f.height));
|
|
405
451
|
}
|
|
406
452
|
}
|
|
407
453
|
}
|
|
@@ -414,26 +460,26 @@ class m {
|
|
|
414
460
|
*/
|
|
415
461
|
static hasLODLevelAvailable(t, e) {
|
|
416
462
|
if (Array.isArray(t)) {
|
|
417
|
-
for (const
|
|
418
|
-
if (this.hasLODLevelAvailable(
|
|
463
|
+
for (const n of t)
|
|
464
|
+
if (this.hasLODLevelAvailable(n, e)) return !0;
|
|
419
465
|
return !1;
|
|
420
466
|
}
|
|
421
467
|
if (t.isMaterial === !0) {
|
|
422
|
-
for (const
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
468
|
+
for (const n of Object.keys(t)) {
|
|
469
|
+
const o = t[n];
|
|
470
|
+
if (o && o.isTexture && this.hasLODLevelAvailable(o, e))
|
|
425
471
|
return !0;
|
|
426
472
|
}
|
|
427
473
|
return !1;
|
|
428
474
|
} else if (t.isGroup === !0) {
|
|
429
|
-
for (const
|
|
430
|
-
if (
|
|
475
|
+
for (const n of t.children)
|
|
476
|
+
if (n.isMesh === !0 && this.hasLODLevelAvailable(n, e))
|
|
431
477
|
return !0;
|
|
432
478
|
}
|
|
433
479
|
let s, r;
|
|
434
480
|
if (t.isMesh ? s = t.geometry : (t.isBufferGeometry || t.isTexture) && (s = t), s && s?.userData?.LODS) {
|
|
435
|
-
const
|
|
436
|
-
if (r = this.lodInfos.get(
|
|
481
|
+
const n = s.userData.LODS;
|
|
482
|
+
if (r = this.lodInfos.get(n.key), e === void 0) return r != null;
|
|
437
483
|
if (r)
|
|
438
484
|
return Array.isArray(r.lods) ? e < r.lods.length : e === 0;
|
|
439
485
|
}
|
|
@@ -453,74 +499,80 @@ class m {
|
|
|
453
499
|
* });
|
|
454
500
|
* ```
|
|
455
501
|
*/
|
|
456
|
-
static assignMeshLOD(t, e) {
|
|
502
|
+
static assignMeshLOD(t, e, s) {
|
|
457
503
|
if (!t) return Promise.resolve(null);
|
|
458
|
-
if (t instanceof
|
|
459
|
-
const
|
|
460
|
-
if (!
|
|
504
|
+
if (t instanceof Y || t.isMesh === !0) {
|
|
505
|
+
const r = t.geometry, n = this.getAssignedLODInformation(r);
|
|
506
|
+
if (!n)
|
|
461
507
|
return Promise.resolve(null);
|
|
462
|
-
for (const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
508
|
+
for (const l of U)
|
|
509
|
+
l.onBeforeGetLODMesh?.(t, e);
|
|
510
|
+
t["LOD:requested level"] = e;
|
|
511
|
+
const o = () => t["LOD:requested level"] === e || this.shouldApplyStaleMeshLOD(t, e);
|
|
512
|
+
return p.getOrLoadLOD(r, e, {
|
|
513
|
+
isCurrent: o
|
|
514
|
+
}).then((l) => {
|
|
515
|
+
if (Array.isArray(l)) {
|
|
516
|
+
const u = n.index || 0;
|
|
517
|
+
l = l[u];
|
|
468
518
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
519
|
+
const a = t["LOD:requested level"] === e;
|
|
520
|
+
return (a || this.shouldApplyStaleMeshLOD(t, e)) && (a && delete t["LOD:requested level"], l && r != l && (l?.isBufferGeometry ? typeof s?.apply == "function" ? s.apply(l, e, t) : s?.apply !== !1 && (t.geometry = l) : g && console.error("Invalid LOD geometry", l))), l;
|
|
521
|
+
}).catch((l) => (console.error("Error loading mesh LOD", t, l), null));
|
|
522
|
+
} else g && console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh", t);
|
|
472
523
|
return Promise.resolve(null);
|
|
473
524
|
}
|
|
474
|
-
static assignTextureLOD(t, e = 0) {
|
|
525
|
+
static assignTextureLOD(t, e = 0, s) {
|
|
475
526
|
if (!t) return Promise.resolve(null);
|
|
527
|
+
const r = s?.force === !0;
|
|
476
528
|
if (t.isMesh === !0) {
|
|
477
|
-
const
|
|
478
|
-
if (Array.isArray(
|
|
479
|
-
const
|
|
480
|
-
for (const
|
|
481
|
-
const
|
|
482
|
-
|
|
529
|
+
const n = t;
|
|
530
|
+
if (Array.isArray(n.material)) {
|
|
531
|
+
const o = new Array();
|
|
532
|
+
for (const l of n.material) {
|
|
533
|
+
const a = this.assignTextureLOD(l, e, s);
|
|
534
|
+
o.push(a);
|
|
483
535
|
}
|
|
484
|
-
return Promise.all(
|
|
485
|
-
const
|
|
486
|
-
for (const
|
|
487
|
-
Array.isArray(
|
|
488
|
-
return
|
|
536
|
+
return Promise.all(o).then((l) => {
|
|
537
|
+
const a = new Array();
|
|
538
|
+
for (const u of l)
|
|
539
|
+
Array.isArray(u) && a.push(...u);
|
|
540
|
+
return a;
|
|
489
541
|
});
|
|
490
542
|
} else
|
|
491
|
-
return this.assignTextureLOD(
|
|
543
|
+
return this.assignTextureLOD(n.material, e, s);
|
|
492
544
|
}
|
|
493
545
|
if (t.isMaterial === !0) {
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
496
|
-
const
|
|
497
|
-
for (const
|
|
498
|
-
const
|
|
499
|
-
if (
|
|
500
|
-
const
|
|
501
|
-
|
|
546
|
+
const n = t, o = [], l = new Array();
|
|
547
|
+
if (this.trackCurrentMaterialTextureSlots(n), n.uniforms && (n.isRawShaderMaterial || n.isShaderMaterial === !0)) {
|
|
548
|
+
const a = n;
|
|
549
|
+
for (const u of Object.keys(a.uniforms)) {
|
|
550
|
+
const d = a.uniforms[u].value;
|
|
551
|
+
if (d?.isTexture === !0) {
|
|
552
|
+
const c = this.assignTextureLODForSlot(d, e, n, u, r).then((f) => (f && a.uniforms[u].value != f && (a.uniforms[u].value = f, a.uniformsNeedUpdate = !0), f));
|
|
553
|
+
o.push(c), l.push(u);
|
|
502
554
|
}
|
|
503
555
|
}
|
|
504
556
|
} else
|
|
505
|
-
for (const
|
|
506
|
-
const
|
|
507
|
-
if (
|
|
508
|
-
const
|
|
509
|
-
|
|
557
|
+
for (const a of Object.keys(n)) {
|
|
558
|
+
const u = n[a];
|
|
559
|
+
if (u?.isTexture === !0) {
|
|
560
|
+
const d = this.assignTextureLODForSlot(u, e, n, a, r);
|
|
561
|
+
o.push(d), l.push(a);
|
|
510
562
|
}
|
|
511
563
|
}
|
|
512
|
-
return Promise.all(
|
|
513
|
-
const
|
|
514
|
-
for (let
|
|
515
|
-
const
|
|
516
|
-
|
|
564
|
+
return Promise.all(o).then((a) => {
|
|
565
|
+
const u = new Array();
|
|
566
|
+
for (let d = 0; d < a.length; d++) {
|
|
567
|
+
const c = a[d], f = l[d];
|
|
568
|
+
c && c.isTexture === !0 ? u.push({ material: n, slot: f, texture: c, level: e }) : u.push({ material: n, slot: f, texture: null, level: e });
|
|
517
569
|
}
|
|
518
|
-
return
|
|
570
|
+
return u;
|
|
519
571
|
});
|
|
520
572
|
}
|
|
521
|
-
if (t instanceof
|
|
522
|
-
const
|
|
523
|
-
return this.assignTextureLODForSlot(
|
|
573
|
+
if (t instanceof W || t.isTexture === !0) {
|
|
574
|
+
const n = t;
|
|
575
|
+
return this.assignTextureLODForSlot(n, e, null, null, r);
|
|
524
576
|
}
|
|
525
577
|
return Promise.resolve(null);
|
|
526
578
|
}
|
|
@@ -529,46 +581,144 @@ class m {
|
|
|
529
581
|
* @default 50 on desktop, 20 on mobile devices
|
|
530
582
|
*/
|
|
531
583
|
static set maxConcurrentLoadingTasks(t) {
|
|
532
|
-
|
|
584
|
+
p.queue.maxConcurrent = t;
|
|
533
585
|
}
|
|
534
586
|
static get maxConcurrentLoadingTasks() {
|
|
535
|
-
return
|
|
587
|
+
return p.queue.maxConcurrent;
|
|
536
588
|
}
|
|
537
589
|
// #region INTERNAL
|
|
538
|
-
static assignTextureLODForSlot(t, e, s, r) {
|
|
539
|
-
|
|
540
|
-
|
|
590
|
+
static assignTextureLODForSlot(t, e, s, r, n) {
|
|
591
|
+
if (t?.isTexture !== !0)
|
|
592
|
+
return Promise.resolve(null);
|
|
593
|
+
if (r === "glyphMap")
|
|
594
|
+
return Promise.resolve(t);
|
|
595
|
+
const o = this.getAssignedLODInformation(t);
|
|
596
|
+
if (o && (o.level === e || !n && o.level < e))
|
|
597
|
+
return Promise.resolve(t);
|
|
598
|
+
if (s && r) {
|
|
599
|
+
const c = this.getPendingTextureSlotRequest(s, r);
|
|
600
|
+
if (c && c.level === e && c.force === n)
|
|
601
|
+
return c.promise;
|
|
602
|
+
}
|
|
603
|
+
const l = s && r ? this.nextTextureSlotRequestId(s, r, e, n) : 0, a = () => !s || !r || this.getLatestTextureSlotRequest(s, r)?.id === l, u = () => a() || this.shouldApplyStaleTextureSlotLOD(s, r, e, n), d = p.getOrLoadLOD(t, e, {
|
|
604
|
+
isCurrent: u
|
|
605
|
+
}).then((c) => {
|
|
606
|
+
if (!a() && !this.shouldApplyStaleTextureSlotLOD(s, r, e, n)) return null;
|
|
607
|
+
if (Array.isArray(c))
|
|
541
608
|
return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."), null;
|
|
542
|
-
if (
|
|
543
|
-
if (
|
|
544
|
-
const
|
|
545
|
-
if (
|
|
546
|
-
const
|
|
547
|
-
if (
|
|
548
|
-
return
|
|
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);
|
|
609
|
+
if (c?.isTexture === !0) {
|
|
610
|
+
if (c != t && s && r) {
|
|
611
|
+
const f = this.getMaterialTextureSlot(s, r) ?? t;
|
|
612
|
+
if (f && !n) {
|
|
613
|
+
const h = this.getAssignedLODInformation(f);
|
|
614
|
+
if (h && h?.level < e)
|
|
615
|
+
return g === "verbose" && console.warn("Assigned texture level is already higher: ", h.level, e, s, f, c), null;
|
|
553
616
|
}
|
|
554
|
-
s
|
|
617
|
+
this.assignTrackedTextureSlot(s, r, c);
|
|
555
618
|
}
|
|
556
|
-
return
|
|
557
|
-
} else
|
|
619
|
+
return c;
|
|
620
|
+
} else g == "verbose" && console.warn("No LOD found for", t, e);
|
|
558
621
|
return null;
|
|
559
|
-
}).catch((
|
|
622
|
+
}).catch((c) => (console.error("Error loading LOD", t, c), null));
|
|
623
|
+
return s && r && this.setPendingTextureSlotRequest(s, r, e, n, l, d), d;
|
|
624
|
+
}
|
|
625
|
+
// Track material slots, not just texture objects. A shared fallback texture can be
|
|
626
|
+
// referenced by many slots and should only be disposed after every slot moved away.
|
|
627
|
+
static trackedTextureSlots = /* @__PURE__ */ new WeakMap();
|
|
628
|
+
static pendingTextureSlotRequests = /* @__PURE__ */ new WeakMap();
|
|
629
|
+
static latestTextureSlotRequests = /* @__PURE__ */ new WeakMap();
|
|
630
|
+
static textureSlotRequestId = 0;
|
|
631
|
+
static trackCurrentMaterialTextureSlots(t) {
|
|
632
|
+
if (t.uniforms && (t.isRawShaderMaterial || t.isShaderMaterial === !0)) {
|
|
633
|
+
const e = t;
|
|
634
|
+
for (const s of Object.keys(e.uniforms)) {
|
|
635
|
+
const r = e.uniforms[s].value;
|
|
636
|
+
r?.isTexture === !0 && this.ensureTrackedTextureSlot(t, s, r);
|
|
637
|
+
}
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
for (const e of Object.keys(t)) {
|
|
641
|
+
const s = t[e];
|
|
642
|
+
s?.isTexture === !0 && this.ensureTrackedTextureSlot(t, e, s);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
static getPendingTextureSlotRequest(t, e) {
|
|
646
|
+
return this.pendingTextureSlotRequests.get(t)?.get(e);
|
|
647
|
+
}
|
|
648
|
+
static nextTextureSlotRequestId(t, e, s, r) {
|
|
649
|
+
let n = this.latestTextureSlotRequests.get(t);
|
|
650
|
+
n || (n = /* @__PURE__ */ new Map(), this.latestTextureSlotRequests.set(t, n));
|
|
651
|
+
const o = ++this.textureSlotRequestId;
|
|
652
|
+
return n.set(e, { id: o, level: s, force: r }), o;
|
|
653
|
+
}
|
|
654
|
+
static getLatestTextureSlotRequest(t, e) {
|
|
655
|
+
return this.latestTextureSlotRequests.get(t)?.get(e);
|
|
656
|
+
}
|
|
657
|
+
static shouldApplyStaleTextureSlotLOD(t, e, s, r) {
|
|
658
|
+
if (!t || !e) return !1;
|
|
659
|
+
const n = this.getLatestTextureSlotRequest(t, e), o = this.getMaterialTextureSlot(t, e), l = this.getAssignedLODInformation(o)?.level ?? 1 / 0;
|
|
660
|
+
return s >= l ? !1 : r ? n ? s >= n.level : !1 : !0;
|
|
661
|
+
}
|
|
662
|
+
static shouldApplyStaleMeshLOD(t, e) {
|
|
663
|
+
const s = t["LOD:requested level"];
|
|
664
|
+
if (typeof s != "number") return !1;
|
|
665
|
+
const r = this.getAssignedLODInformation(t.geometry)?.level ?? 1 / 0;
|
|
666
|
+
return e < r && e >= s;
|
|
667
|
+
}
|
|
668
|
+
static setPendingTextureSlotRequest(t, e, s, r, n, o) {
|
|
669
|
+
let l = this.pendingTextureSlotRequests.get(t);
|
|
670
|
+
l || (l = /* @__PURE__ */ new Map(), this.pendingTextureSlotRequests.set(t, l));
|
|
671
|
+
const a = { level: s, force: r, id: n, promise: o };
|
|
672
|
+
l.set(e, a), o.finally(() => {
|
|
673
|
+
l.get(e)?.id === n && l.delete(e);
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
static getMaterialTextureSlot(t, e) {
|
|
677
|
+
const r = t.uniforms?.[e];
|
|
678
|
+
if (r?.value?.isTexture === !0)
|
|
679
|
+
return r.value;
|
|
680
|
+
const n = t[e];
|
|
681
|
+
return n?.isTexture === !0 ? n : null;
|
|
682
|
+
}
|
|
683
|
+
static setMaterialTextureSlot(t, e, s) {
|
|
684
|
+
const n = t.uniforms?.[e];
|
|
685
|
+
if (n?.value?.isTexture === !0) {
|
|
686
|
+
n.value = s, t.uniformsNeedUpdate = !0;
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
t[e] = s;
|
|
690
|
+
}
|
|
691
|
+
static assignTrackedTextureSlot(t, e, s) {
|
|
692
|
+
let r = this.trackedTextureSlots.get(t);
|
|
693
|
+
r || (r = /* @__PURE__ */ new Map(), this.trackedTextureSlots.set(t, r));
|
|
694
|
+
const n = this.getMaterialTextureSlot(t, e);
|
|
695
|
+
let o = r.get(e);
|
|
696
|
+
!o && n ? o = this.ensureTrackedTextureSlot(t, e, n) : o && n && o !== n && (this.releaseTrackedTextureSlot(t, e, o), o = this.ensureTrackedTextureSlot(t, e, n)), !(o === s && n === s) && (o && o !== s && this.releaseTrackedTextureSlot(t, e, o), o !== s && (this.trackTextureUsage(s), r.set(e, s)), n !== s && this.setMaterialTextureSlot(t, e, s));
|
|
697
|
+
}
|
|
698
|
+
static ensureTrackedTextureSlot(t, e, s) {
|
|
699
|
+
let r = this.trackedTextureSlots.get(t);
|
|
700
|
+
r || (r = /* @__PURE__ */ new Map(), this.trackedTextureSlots.set(t, r));
|
|
701
|
+
const n = r.get(e);
|
|
702
|
+
return n === s ? n : (n && this.releaseTrackedTextureSlot(t, e, n), this.trackTextureUsage(s), r.set(e, s), s);
|
|
703
|
+
}
|
|
704
|
+
static releaseTrackedTextureSlot(t, e, s) {
|
|
705
|
+
const r = this.trackedTextureSlots.get(t);
|
|
706
|
+
if (r?.get(e) === s && r.delete(e), this.untrackTextureUsage(s) && (g || ie)) {
|
|
707
|
+
const o = this.getAssignedLODInformation(s);
|
|
708
|
+
console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level ?? "?"} for ${t.name || t.type}.${e}`, s.uuid);
|
|
709
|
+
}
|
|
560
710
|
}
|
|
561
711
|
parser;
|
|
562
712
|
url;
|
|
563
713
|
constructor(t) {
|
|
564
714
|
const e = t.options.path;
|
|
565
|
-
|
|
715
|
+
g && console.log("Progressive extension registered for", e), this.parser = t, this.url = e;
|
|
566
716
|
}
|
|
567
717
|
_isLoadingMesh;
|
|
568
718
|
loadMesh = (t) => {
|
|
569
719
|
if (this._isLoadingMesh) return null;
|
|
570
|
-
const e = this.parser.json.meshes[t]?.extensions?.[
|
|
571
|
-
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s &&
|
|
720
|
+
const e = this.parser.json.meshes[t]?.extensions?.[z];
|
|
721
|
+
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && p.registerMesh(this.url, e.guid, s, e.lods?.length, 0, e), s))) : null;
|
|
572
722
|
};
|
|
573
723
|
// private _isLoadingTexture;
|
|
574
724
|
// loadTexture = (textureIndex: number) => {
|
|
@@ -585,65 +735,83 @@ class m {
|
|
|
585
735
|
// });
|
|
586
736
|
// }
|
|
587
737
|
afterRoot(t) {
|
|
588
|
-
return
|
|
738
|
+
return g && console.log("AFTER", this.url, t), this.parser.json.textures?.forEach((e, s) => {
|
|
589
739
|
if (e?.extensions) {
|
|
590
|
-
const r = e?.extensions[
|
|
740
|
+
const r = e?.extensions[z];
|
|
591
741
|
if (r) {
|
|
592
742
|
if (!r.lods) {
|
|
593
|
-
|
|
743
|
+
g && console.warn("Texture has no LODs", r);
|
|
594
744
|
return;
|
|
595
745
|
}
|
|
596
|
-
let
|
|
597
|
-
for (const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
746
|
+
let n = !1;
|
|
747
|
+
for (const o of this.parser.associations.keys())
|
|
748
|
+
o.isTexture === !0 && this.parser.associations.get(o)?.textures === s && (n = !0, p.registerTexture(this.url, o, r.lods?.length, s, r));
|
|
749
|
+
n || this.parser.getDependency("texture", s).then((o) => {
|
|
750
|
+
o && p.registerTexture(this.url, o, r.lods?.length, s, r);
|
|
601
751
|
});
|
|
602
752
|
}
|
|
603
753
|
}
|
|
604
754
|
}), this.parser.json.meshes?.forEach((e, s) => {
|
|
605
755
|
if (e?.extensions) {
|
|
606
|
-
const r = e?.extensions[
|
|
756
|
+
const r = e?.extensions[z];
|
|
607
757
|
if (r && r.lods) {
|
|
608
|
-
for (const
|
|
609
|
-
if (
|
|
610
|
-
const
|
|
611
|
-
|
|
758
|
+
for (const n of this.parser.associations.keys())
|
|
759
|
+
if (n.isMesh) {
|
|
760
|
+
const o = this.parser.associations.get(n);
|
|
761
|
+
o?.meshes === s && p.registerMesh(this.url, r.guid, n, r.lods.length, o.primitives, r);
|
|
612
762
|
}
|
|
613
763
|
}
|
|
614
764
|
}
|
|
615
765
|
}), null;
|
|
616
766
|
}
|
|
617
767
|
/**
|
|
618
|
-
* Register a texture with LOD information
|
|
768
|
+
* Register a texture with progressive LOD information. This associates the texture with its LOD extension data
|
|
769
|
+
* so the LODs manager can later swap it for higher or lower resolution versions based on screen coverage.
|
|
770
|
+
* Typically called during glTF loading when the progressive extension is parsed.
|
|
771
|
+
*
|
|
772
|
+
* @param url - The source URL of the glTF file this texture was loaded from.
|
|
773
|
+
* @param tex - The three.js Texture instance to register.
|
|
774
|
+
* @param level - The LOD level this texture represents (0 = highest resolution).
|
|
775
|
+
* @param index - The texture index within the glTF file.
|
|
776
|
+
* @param ext - The parsed progressive texture extension data containing all available LOD levels and their dimensions.
|
|
619
777
|
*/
|
|
620
|
-
static registerTexture = (t, e, s, r,
|
|
778
|
+
static registerTexture = (t, e, s, r, n) => {
|
|
621
779
|
if (!e) {
|
|
622
|
-
|
|
780
|
+
g && console.error("!! gltf-progressive: Called register texture without texture");
|
|
623
781
|
return;
|
|
624
782
|
}
|
|
625
|
-
if (
|
|
626
|
-
const { width: l, height: a } =
|
|
627
|
-
console.log(`> gltf-progressive: register texture[${r}] "${e.name || e.uuid}", Current: ${l}x${a}, Max: ${
|
|
783
|
+
if (g) {
|
|
784
|
+
const { width: l, height: a } = ve(e);
|
|
785
|
+
console.log(`> gltf-progressive: register texture[${r}] "${e.name || e.uuid}", Current: ${l}x${a}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`, n, e);
|
|
628
786
|
}
|
|
629
|
-
e.source && (e.source[
|
|
630
|
-
const
|
|
631
|
-
|
|
787
|
+
e.source && (e.source[he] = n);
|
|
788
|
+
const o = n.guid;
|
|
789
|
+
p.assignLODInformation(t, e, o, s, r), p.lodInfos.set(o, n), p.lowresCache.set(o, new WeakRef(e));
|
|
632
790
|
};
|
|
633
791
|
/**
|
|
634
|
-
* Register a mesh with LOD information
|
|
792
|
+
* Register a mesh with progressive LOD information. This associates the mesh geometry with its LOD extension data
|
|
793
|
+
* so the LODs manager can later swap it for higher or lower density versions based on screen coverage.
|
|
794
|
+
* Typically called during glTF loading when the progressive extension is parsed.
|
|
795
|
+
* If the mesh is registered at a level > 0 (i.e. not full resolution), a raycast mesh is automatically preserved for accurate picking.
|
|
796
|
+
*
|
|
797
|
+
* @param url - The source URL of the glTF file this mesh was loaded from.
|
|
798
|
+
* @param key - A unique key identifying this mesh's LOD group (typically derived from the extension GUID).
|
|
799
|
+
* @param mesh - The three.js Mesh instance to register.
|
|
800
|
+
* @param level - The LOD level this mesh represents (0 = highest resolution / full density).
|
|
801
|
+
* @param index - The primitive index within the glTF mesh node.
|
|
802
|
+
* @param ext - The parsed progressive mesh extension data containing all available LOD levels with vertex/index counts and densities.
|
|
635
803
|
*/
|
|
636
|
-
static registerMesh = (t, e, s, r,
|
|
804
|
+
static registerMesh = (t, e, s, r, n, o) => {
|
|
637
805
|
const l = s.geometry;
|
|
638
806
|
if (!l) {
|
|
639
|
-
|
|
807
|
+
g && console.warn("gltf-progressive: Register mesh without geometry");
|
|
640
808
|
return;
|
|
641
809
|
}
|
|
642
|
-
l.userData || (l.userData = {}),
|
|
643
|
-
let u =
|
|
644
|
-
u ? u.push(s.geometry) : u = [s.geometry],
|
|
645
|
-
for (const
|
|
646
|
-
|
|
810
|
+
l.userData || (l.userData = {}), g && console.log("> Progressive: register mesh " + s.name, { index: n, uuid: s.uuid }, o, s), p.assignLODInformation(t, l, e, r, n), p.lodInfos.set(e, o);
|
|
811
|
+
let u = p.lowresCache.get(e)?.deref();
|
|
812
|
+
u ? u.push(s.geometry) : u = [s.geometry], p.lowresCache.set(e, new WeakRef(u)), r > 0 && !ae(s) && _t(s, l);
|
|
813
|
+
for (const d of U)
|
|
814
|
+
d.onRegisteredNewMesh?.(s, o);
|
|
647
815
|
};
|
|
648
816
|
/**
|
|
649
817
|
* Dispose cached resources to free memory.
|
|
@@ -670,7 +838,7 @@ class m {
|
|
|
670
838
|
for (const [s, r] of this.cache)
|
|
671
839
|
s.includes(t) && (this._disposeCacheEntry(r), this.cache.delete(s));
|
|
672
840
|
} else {
|
|
673
|
-
this.lodInfos.clear();
|
|
841
|
+
this.cacheGeneration++, this.lodInfos.clear();
|
|
674
842
|
for (const [, e] of this.lowresCache) {
|
|
675
843
|
const s = e.deref();
|
|
676
844
|
if (s) {
|
|
@@ -684,7 +852,7 @@ class m {
|
|
|
684
852
|
this.lowresCache.clear();
|
|
685
853
|
for (const [, e] of this.cache)
|
|
686
854
|
this._disposeCacheEntry(e);
|
|
687
|
-
this.cache.clear(), this.textureRefCounts.clear();
|
|
855
|
+
this.cache.clear(), this.textureRefCounts.clear(), this.trackedTextureSlots = /* @__PURE__ */ new WeakMap(), this.pendingTextureSlotRequests = /* @__PURE__ */ new WeakMap(), this.latestTextureSlotRequests = /* @__PURE__ */ new WeakMap(), this.textureSlotRequestId = 0;
|
|
688
856
|
}
|
|
689
857
|
}
|
|
690
858
|
/** Dispose a single cache entry's three.js resource(s) to free GPU memory. */
|
|
@@ -693,7 +861,7 @@ class m {
|
|
|
693
861
|
const e = t.deref();
|
|
694
862
|
e && (e.isTexture && this.textureRefCounts.delete(e.uuid), e.dispose());
|
|
695
863
|
} else
|
|
696
|
-
t.then((e) => {
|
|
864
|
+
this.disposedRequests.add(t), t.then((e) => {
|
|
697
865
|
if (e)
|
|
698
866
|
if (Array.isArray(e))
|
|
699
867
|
for (const s of e) s.dispose();
|
|
@@ -716,16 +884,16 @@ class m {
|
|
|
716
884
|
* The held value is the cache key string used in `previouslyLoaded`.
|
|
717
885
|
*/
|
|
718
886
|
static _resourceRegistry = new FinalizationRegistry((t) => {
|
|
719
|
-
const e =
|
|
720
|
-
(
|
|
721
|
-
${t}`), e instanceof WeakRef && (e.deref() || (
|
|
887
|
+
const e = p.cache.get(t);
|
|
888
|
+
(g || ie) && console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
889
|
+
${t}`), e instanceof WeakRef && (e.deref() || (p.cache.delete(t), (g || ie) && console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")));
|
|
722
890
|
});
|
|
723
891
|
/**
|
|
724
892
|
* Track texture usage by incrementing reference count
|
|
725
893
|
*/
|
|
726
894
|
static trackTextureUsage(t) {
|
|
727
895
|
const e = t.uuid, s = this.textureRefCounts.get(e) || 0;
|
|
728
|
-
this.textureRefCounts.set(e, s + 1),
|
|
896
|
+
this.textureRefCounts.set(e, s + 1), g === "verbose" && console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s + 1}`);
|
|
729
897
|
}
|
|
730
898
|
/**
|
|
731
899
|
* Untrack texture usage by decrementing reference count.
|
|
@@ -735,182 +903,209 @@ ${t}`), e instanceof WeakRef && (e.deref() || (m.cache.delete(t), (f || K) && co
|
|
|
735
903
|
static untrackTextureUsage(t) {
|
|
736
904
|
const e = t.uuid, s = this.textureRefCounts.get(e);
|
|
737
905
|
if (!s)
|
|
738
|
-
return (
|
|
906
|
+
return (g === "verbose" || ie) && n("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)", 0), t.dispose(), !0;
|
|
739
907
|
const r = s - 1;
|
|
740
908
|
if (r <= 0)
|
|
741
|
-
return this.textureRefCounts.delete(e), (
|
|
742
|
-
return this.textureRefCounts.set(e, r),
|
|
743
|
-
function o
|
|
744
|
-
let { width: a, height: u } =
|
|
745
|
-
const
|
|
746
|
-
let
|
|
747
|
-
a && u && (
|
|
909
|
+
return this.textureRefCounts.delete(e), (g || ie) && n("[gltf-progressive] Memory: Dispose texture", r), t.dispose(), !0;
|
|
910
|
+
return this.textureRefCounts.set(e, r), g === "verbose" && n("[gltf-progressive] Memory: Untrack texture", r), !1;
|
|
911
|
+
function n(o, l) {
|
|
912
|
+
let { width: a, height: u } = ve(t);
|
|
913
|
+
const d = a && u ? `${a}x${u}` : "N/A";
|
|
914
|
+
let c = "N/A";
|
|
915
|
+
a && u && (c = `~${(vt(t) / (1024 * 1024)).toFixed(2)} MB`), console.log(`${o} — ${t.name} ${d} (${c}), refCount: ${s} → ${l}
|
|
748
916
|
${e}`);
|
|
749
917
|
}
|
|
750
918
|
}
|
|
751
919
|
static workers = [];
|
|
920
|
+
static cacheGeneration = 0;
|
|
921
|
+
static disposedRequests = /* @__PURE__ */ new WeakSet();
|
|
752
922
|
static _workersIndex = 0;
|
|
753
|
-
static async getOrLoadLOD(t, e) {
|
|
754
|
-
const
|
|
755
|
-
if (!
|
|
756
|
-
return
|
|
757
|
-
const
|
|
758
|
-
let
|
|
923
|
+
static async getOrLoadLOD(t, e, s) {
|
|
924
|
+
const r = this.cacheGeneration, n = g == "verbose", o = this.getAssignedLODInformation(t);
|
|
925
|
+
if (!o)
|
|
926
|
+
return g && console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`, t), null;
|
|
927
|
+
const l = o?.key;
|
|
928
|
+
let a;
|
|
759
929
|
if (t.isTexture === !0) {
|
|
760
|
-
const
|
|
761
|
-
|
|
930
|
+
const d = t;
|
|
931
|
+
d.source && d.source[he] && (a = d.source[he]);
|
|
762
932
|
}
|
|
763
|
-
if (
|
|
764
|
-
|
|
933
|
+
if (a || (a = p.lodInfos.get(l)), !a)
|
|
934
|
+
g && console.warn(`Can not load LOD ${e}: no LOD info found for "${l}" ${t.name}`, t.type, p.lodInfos);
|
|
765
935
|
else {
|
|
766
936
|
if (e > 0) {
|
|
767
|
-
let
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
770
|
-
const
|
|
771
|
-
if (
|
|
772
|
-
const
|
|
773
|
-
if (
|
|
774
|
-
this.lowresCache.delete(
|
|
937
|
+
let f = !1;
|
|
938
|
+
const h = Array.isArray(a.lods);
|
|
939
|
+
if (h && e >= a.lods.length ? f = !0 : h || (f = !0), f) {
|
|
940
|
+
const D = this.lowresCache.get(l);
|
|
941
|
+
if (D) {
|
|
942
|
+
const w = D.deref();
|
|
943
|
+
if (w) return w;
|
|
944
|
+
this.lowresCache.delete(l), g && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${l}`);
|
|
775
945
|
}
|
|
776
946
|
return null;
|
|
777
947
|
}
|
|
778
948
|
}
|
|
779
|
-
const
|
|
780
|
-
if (!
|
|
781
|
-
return
|
|
782
|
-
const
|
|
783
|
-
if (
|
|
784
|
-
if (!
|
|
785
|
-
return console.warn("missing pointer for glb/gltf texture",
|
|
786
|
-
const
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
949
|
+
const d = Array.isArray(a.lods) ? a.lods[e]?.path : a.lods;
|
|
950
|
+
if (!d)
|
|
951
|
+
return g && !a["missing:uri"] && (a["missing:uri"] = !0, console.warn("Missing uri for progressive asset for LOD " + e, a)), null;
|
|
952
|
+
const c = wt(o.url, d);
|
|
953
|
+
if (c.endsWith(".glb") || c.endsWith(".gltf")) {
|
|
954
|
+
if (!a.guid)
|
|
955
|
+
return console.warn("missing pointer for glb/gltf texture", a), null;
|
|
956
|
+
const f = c + "_" + a.guid, h = await this.tryResolveLODCacheEntry(this.cache.get(f), f, c, t, e, n);
|
|
957
|
+
if (r !== this.cacheGeneration) return null;
|
|
958
|
+
if (h.found) return h.value;
|
|
959
|
+
if (s?.isCurrent?.() === !1)
|
|
960
|
+
return n && console.log(`Skipping stale LOD ${e} request before queue: ${c}`), null;
|
|
961
|
+
const D = await this.queue.slot(c);
|
|
962
|
+
if (r !== this.cacheGeneration) return null;
|
|
963
|
+
if (s?.isCurrent?.() === !1)
|
|
964
|
+
return n && console.log(`Skipping stale LOD ${e} request after queue: ${c}`), null;
|
|
965
|
+
const w = await this.tryResolveLODCacheEntry(this.cache.get(f), f, c, t, e, n);
|
|
966
|
+
if (r !== this.cacheGeneration) return null;
|
|
967
|
+
if (w.found) return w.value;
|
|
968
|
+
const x = this.cache.get(f);
|
|
969
|
+
if (x !== void 0) {
|
|
970
|
+
const y = await this.tryResolveLODCacheEntry(x, f, c, t, e, n);
|
|
971
|
+
if (r !== this.cacheGeneration) return null;
|
|
972
|
+
if (y.found) return y.value;
|
|
973
|
+
}
|
|
974
|
+
if (!D.use)
|
|
975
|
+
return g && console.log(`LOD ${e} was aborted: ${c}`), null;
|
|
976
|
+
const L = a, M = new Promise(async (y, O) => {
|
|
977
|
+
if (Ct) {
|
|
978
|
+
const m = await (await Rt({})).load(c);
|
|
979
|
+
if (m.textures.length > 0)
|
|
980
|
+
for (const v of m.textures) {
|
|
981
|
+
let S = v.texture;
|
|
982
|
+
return p.assignLODInformation(o.url, S, l, e, void 0), t instanceof W && (S = this.copySettings(t, S)), S && (S.guid = L.guid), y(S);
|
|
811
983
|
}
|
|
812
|
-
if (
|
|
813
|
-
const
|
|
814
|
-
for (const
|
|
815
|
-
const
|
|
816
|
-
|
|
984
|
+
if (m.geometries.length > 0) {
|
|
985
|
+
const v = new Array();
|
|
986
|
+
for (const S of m.geometries) {
|
|
987
|
+
const j = S.geometry;
|
|
988
|
+
p.assignLODInformation(o.url, j, l, e, S.primitiveIndex), v.push(j);
|
|
817
989
|
}
|
|
818
|
-
return
|
|
990
|
+
return y(v);
|
|
819
991
|
}
|
|
820
|
-
return
|
|
992
|
+
return y(null);
|
|
821
993
|
}
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
let
|
|
825
|
-
if (
|
|
826
|
-
const
|
|
827
|
-
|
|
994
|
+
const I = new Me();
|
|
995
|
+
qe(I), g && (await new Promise((b) => setTimeout(b, 1e3)), n && console.warn("Start loading (delayed) " + c, L.guid));
|
|
996
|
+
let P = c;
|
|
997
|
+
if (L && Array.isArray(L.lods)) {
|
|
998
|
+
const b = L.lods[e];
|
|
999
|
+
b.hash && (P += "?v=" + b.hash);
|
|
828
1000
|
}
|
|
829
|
-
const
|
|
830
|
-
`,
|
|
831
|
-
if (!
|
|
832
|
-
return
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
let
|
|
836
|
-
if (
|
|
837
|
-
let
|
|
838
|
-
for (const
|
|
839
|
-
if (
|
|
840
|
-
const
|
|
841
|
-
if (
|
|
842
|
-
|
|
1001
|
+
const _ = await I.loadAsync(P).catch((b) => (console.error(`Error loading LOD ${e} from ${c}
|
|
1002
|
+
`, b), y(null)));
|
|
1003
|
+
if (!_)
|
|
1004
|
+
return y(null);
|
|
1005
|
+
const E = _.parser;
|
|
1006
|
+
n && console.log("Loading finished " + c, L.guid);
|
|
1007
|
+
let C = 0;
|
|
1008
|
+
if (_.parser.json.textures) {
|
|
1009
|
+
let b = !1;
|
|
1010
|
+
for (const m of _.parser.json.textures) {
|
|
1011
|
+
if (m?.extensions) {
|
|
1012
|
+
const v = m?.extensions[z];
|
|
1013
|
+
if (v?.guid && v.guid === L.guid) {
|
|
1014
|
+
b = !0;
|
|
843
1015
|
break;
|
|
844
1016
|
}
|
|
845
1017
|
}
|
|
846
|
-
|
|
1018
|
+
C++;
|
|
847
1019
|
}
|
|
848
|
-
if (
|
|
849
|
-
let
|
|
850
|
-
return
|
|
851
|
-
} else
|
|
1020
|
+
if (b) {
|
|
1021
|
+
let m = await E.getDependency("texture", C);
|
|
1022
|
+
return m && p.assignLODInformation(o.url, m, l, e, void 0), n && console.log('change "' + t.name + '" → "' + m.name + '"', c, C, m, f), t instanceof W && (m = this.copySettings(t, m)), m && (m.guid = L.guid), y(m);
|
|
1023
|
+
} else g && console.warn("Could not find texture with guid", L.guid, _.parser.json);
|
|
852
1024
|
}
|
|
853
|
-
if (
|
|
854
|
-
let
|
|
855
|
-
for (const
|
|
856
|
-
if (
|
|
857
|
-
const
|
|
858
|
-
if (
|
|
859
|
-
|
|
1025
|
+
if (C = 0, _.parser.json.meshes) {
|
|
1026
|
+
let b = !1;
|
|
1027
|
+
for (const m of _.parser.json.meshes) {
|
|
1028
|
+
if (m?.extensions) {
|
|
1029
|
+
const v = m?.extensions[z];
|
|
1030
|
+
if (v?.guid && v.guid === L.guid) {
|
|
1031
|
+
b = !0;
|
|
860
1032
|
break;
|
|
861
1033
|
}
|
|
862
1034
|
}
|
|
863
|
-
|
|
1035
|
+
C++;
|
|
864
1036
|
}
|
|
865
|
-
if (
|
|
866
|
-
const
|
|
867
|
-
if (
|
|
868
|
-
const
|
|
869
|
-
return
|
|
1037
|
+
if (b) {
|
|
1038
|
+
const m = await E.getDependency("mesh", C);
|
|
1039
|
+
if (n && console.log(`Loaded Mesh "${m.name}"`, c, C, m, f), m.isMesh === !0) {
|
|
1040
|
+
const v = m.geometry;
|
|
1041
|
+
return p.assignLODInformation(o.url, v, l, e, 0), y(v);
|
|
870
1042
|
} else {
|
|
871
|
-
const
|
|
872
|
-
for (let
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
875
|
-
const
|
|
876
|
-
|
|
1043
|
+
const v = new Array();
|
|
1044
|
+
for (let S = 0; S < m.children.length; S++) {
|
|
1045
|
+
const j = m.children[S];
|
|
1046
|
+
if (j.isMesh === !0) {
|
|
1047
|
+
const J = j.geometry;
|
|
1048
|
+
p.assignLODInformation(o.url, J, l, e, S), v.push(J);
|
|
877
1049
|
}
|
|
878
1050
|
}
|
|
879
|
-
return
|
|
1051
|
+
return y(v);
|
|
880
1052
|
}
|
|
881
|
-
} else
|
|
1053
|
+
} else g && console.warn("Could not find mesh with guid", L.guid, _.parser.json);
|
|
882
1054
|
}
|
|
883
|
-
return
|
|
1055
|
+
return y(null);
|
|
884
1056
|
});
|
|
885
|
-
this.cache.set(
|
|
886
|
-
const
|
|
887
|
-
return M != null ?
|
|
888
|
-
} else if (t instanceof
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1057
|
+
this.cache.set(f, M), D.use(M);
|
|
1058
|
+
const T = await M;
|
|
1059
|
+
return r !== this.cacheGeneration || this.cache.get(f) !== M ? null : (T != null ? T instanceof W ? (this.cache.set(f, new WeakRef(T)), p._resourceRegistry.register(T, f)) : Array.isArray(T) ? this.cache.set(f, Promise.resolve(T)) : this.cache.set(f, Promise.resolve(T)) : this.cache.set(f, Promise.resolve(null)), T);
|
|
1060
|
+
} else if (t instanceof W) {
|
|
1061
|
+
if (s?.isCurrent?.() === !1)
|
|
1062
|
+
return n && console.log(`Skipping stale texture LOD ${e} request: ${c}`), null;
|
|
1063
|
+
n && console.log("Load texture from uri: " + c);
|
|
1064
|
+
const h = await new it().loadAsync(c);
|
|
1065
|
+
return r !== this.cacheGeneration || s?.isCurrent?.() === !1 ? (h?.dispose(), null) : (h ? (h.guid = a.guid, h.flipY = !1, h.needsUpdate = !0, h.colorSpace = t.colorSpace, n && console.log(a, h)) : g && console.warn("failed loading", c), h);
|
|
892
1066
|
}
|
|
893
1067
|
}
|
|
894
1068
|
return null;
|
|
895
1069
|
}
|
|
896
|
-
static
|
|
897
|
-
|
|
1070
|
+
static async tryResolveLODCacheEntry(t, e, s, r, n, o) {
|
|
1071
|
+
if (t === void 0)
|
|
1072
|
+
return { found: !1 };
|
|
1073
|
+
if (o && console.log(`LOD ${n} was already loading/loaded: ${e}`), t instanceof WeakRef) {
|
|
1074
|
+
const u = t.deref();
|
|
1075
|
+
if (u) {
|
|
1076
|
+
let d = u, c = !1;
|
|
1077
|
+
if (d instanceof W && r instanceof W ? Oe(d.image) || Le(d) ? d = this.copySettings(r, d) : c = !0 : d instanceof H && r instanceof H && (d.attributes.position?.array || (c = !0)), !c)
|
|
1078
|
+
return { found: !0, value: d };
|
|
1079
|
+
}
|
|
1080
|
+
return this.cache.delete(e), g && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`), { found: !1 };
|
|
1081
|
+
}
|
|
1082
|
+
let l = await t.catch((u) => (console.error(`Error loading LOD ${n} from ${s}
|
|
1083
|
+
`, u), null));
|
|
1084
|
+
if (this.disposedRequests.has(t)) return { found: !0, value: null };
|
|
1085
|
+
let a = !1;
|
|
1086
|
+
return l == null || (l instanceof W && r instanceof W ? Oe(l.image) || Le(l) ? l = this.copySettings(r, l) : (a = !0, this.cache.delete(e)) : l instanceof H && r instanceof H && (l.attributes.position?.array || (a = !0, this.cache.delete(e)))), a ? { found: !1 } : { found: !0, value: l };
|
|
1087
|
+
}
|
|
1088
|
+
static _queue;
|
|
1089
|
+
static get queue() {
|
|
1090
|
+
return this._queue ??= new Lt(be() ? 20 : 50, { debug: g != !1 });
|
|
1091
|
+
}
|
|
1092
|
+
static assignLODInformation(t, e, s, r, n) {
|
|
898
1093
|
if (!e) return;
|
|
899
1094
|
e.userData || (e.userData = {});
|
|
900
|
-
const
|
|
901
|
-
e.userData.LODS =
|
|
1095
|
+
const o = new kt(t, s, r, n);
|
|
1096
|
+
e.userData.LODS = o, "source" in e && typeof e.source == "object" && (e.source.LODS = o);
|
|
902
1097
|
}
|
|
903
1098
|
static getAssignedLODInformation(t) {
|
|
904
1099
|
return t ? t.userData?.LODS ? t.userData.LODS : "source" in t && t.source?.LODS ? t.source.LODS : null : null;
|
|
905
1100
|
}
|
|
906
1101
|
// private static readonly _copiedTextures: WeakMap<Texture, Texture> = new Map();
|
|
907
1102
|
static copySettings(t, e) {
|
|
908
|
-
return e ? (
|
|
1103
|
+
return e ? (g === "verbose" && console.debug(`Copy texture settings
|
|
909
1104
|
`, t.uuid, `
|
|
910
1105
|
`, 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
1106
|
}
|
|
912
1107
|
}
|
|
913
|
-
class
|
|
1108
|
+
class kt {
|
|
914
1109
|
url;
|
|
915
1110
|
/** the key to lookup the LOD information */
|
|
916
1111
|
key;
|
|
@@ -921,10 +1116,10 @@ class Ot {
|
|
|
921
1116
|
this.url = t, this.key = e, this.level = s, r != null && (this.index = r);
|
|
922
1117
|
}
|
|
923
1118
|
}
|
|
924
|
-
class
|
|
1119
|
+
class le {
|
|
925
1120
|
static addPromise = (t, e, s, r) => {
|
|
926
|
-
r.forEach((
|
|
927
|
-
|
|
1121
|
+
r.forEach((n) => {
|
|
1122
|
+
n.add(t, e, s);
|
|
928
1123
|
});
|
|
929
1124
|
};
|
|
930
1125
|
ready;
|
|
@@ -955,8 +1150,8 @@ class ce {
|
|
|
955
1150
|
_maxPromisesPerObject = 1;
|
|
956
1151
|
constructor(t, e) {
|
|
957
1152
|
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((
|
|
959
|
-
this._resolve =
|
|
1153
|
+
this._frame_start = e.waitForFirstCapture ? void 0 : t, this._frames_to_capture = r, this.ready = new Promise((n) => {
|
|
1154
|
+
this._resolve = n;
|
|
960
1155
|
}), this.ready.finally(() => {
|
|
961
1156
|
this._resolved = !0, this._awaiting.length = 0;
|
|
962
1157
|
}), this._signal = e.signal, this._signal?.addEventListener("abort", () => {
|
|
@@ -970,15 +1165,15 @@ class ce {
|
|
|
970
1165
|
_seen = /* @__PURE__ */ new WeakMap();
|
|
971
1166
|
add(t, e, s) {
|
|
972
1167
|
if (this._resolved) {
|
|
973
|
-
|
|
1168
|
+
g && console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
|
|
974
1169
|
return;
|
|
975
1170
|
}
|
|
976
1171
|
if (!(this._frame_start !== void 0 && this._currentFrame > this._frame_start + this._frames_to_capture)) {
|
|
977
1172
|
if (this._maxPromisesPerObject >= 1)
|
|
978
1173
|
if (this._seen.has(e)) {
|
|
979
|
-
|
|
1174
|
+
const r = this._seen.get(e);
|
|
980
1175
|
if (r >= this._maxPromisesPerObject) {
|
|
981
|
-
|
|
1176
|
+
g && console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");
|
|
982
1177
|
return;
|
|
983
1178
|
}
|
|
984
1179
|
this._seen.set(e, r + 1);
|
|
@@ -997,22 +1192,122 @@ class ce {
|
|
|
997
1192
|
});
|
|
998
1193
|
}
|
|
999
1194
|
}
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1195
|
+
const $ = X("debugprogressive"), At = $ === "colors", It = X("noprogressive"), ge = /* @__PURE__ */ Symbol("Needle:LODSManager"), pe = /* @__PURE__ */ Symbol("Needle:LODState"), N = /* @__PURE__ */ Symbol("Needle:CurrentLOD"), Se = Xe, R = { mesh_lod: -1, texture_lod: -1 }, $t = new $e(), Re = [
|
|
1196
|
+
3526751,
|
|
1197
|
+
11065402,
|
|
1198
|
+
15978811,
|
|
1199
|
+
15897394,
|
|
1200
|
+
15749691,
|
|
1201
|
+
11032304,
|
|
1202
|
+
4827122,
|
|
1203
|
+
3332036,
|
|
1204
|
+
16739229,
|
|
1205
|
+
7306743,
|
|
1206
|
+
14053330,
|
|
1207
|
+
3516499,
|
|
1208
|
+
12035359,
|
|
1209
|
+
14703919,
|
|
1210
|
+
3963096,
|
|
1211
|
+
42662,
|
|
1212
|
+
14100029,
|
|
1213
|
+
8344319,
|
|
1214
|
+
4633680,
|
|
1215
|
+
16229681,
|
|
1216
|
+
3120096,
|
|
1217
|
+
12076434,
|
|
1218
|
+
9083434,
|
|
1219
|
+
2060171,
|
|
1220
|
+
15751837,
|
|
1221
|
+
10182117,
|
|
1222
|
+
48121,
|
|
1223
|
+
62932,
|
|
1224
|
+
16704576,
|
|
1225
|
+
15817653,
|
|
1226
|
+
5083278,
|
|
1227
|
+
5592405
|
|
1228
|
+
];
|
|
1229
|
+
function Gt() {
|
|
1230
|
+
const i = Se.Timer || Se.Clock;
|
|
1231
|
+
return new i();
|
|
1232
|
+
}
|
|
1233
|
+
const me = new fe(), V = new fe(), Pe = new fe(), Bt = new A(), qt = new A(), Wt = new Ge(), F = new A(), Z = new A(), ee = new A(), te = new A();
|
|
1234
|
+
function Ft(i, t) {
|
|
1235
|
+
const e = i.min, s = i.max, r = (e.x + s.x) * 0.5, n = (e.y + s.y) * 0.5;
|
|
1236
|
+
return F.set(r, n, e.z).applyMatrix4(t).z < 0;
|
|
1237
|
+
}
|
|
1238
|
+
function Ut(i) {
|
|
1239
|
+
const {
|
|
1240
|
+
geometry: t,
|
|
1241
|
+
matrixWorld: e,
|
|
1242
|
+
camera: s,
|
|
1243
|
+
projectionScreenMatrix: r,
|
|
1244
|
+
desiredDensity: n,
|
|
1245
|
+
canvasHeight: o = 0,
|
|
1246
|
+
currentLevel: l = -1,
|
|
1247
|
+
xrEnabled: a = !1,
|
|
1248
|
+
debugDrawLine: u,
|
|
1249
|
+
warnMissingPrimitiveDensities: d = !1
|
|
1250
|
+
} = i, c = p.getMeshLODExtension(t)?.lods, f = p.getPrimitiveIndex(t), h = i.target ?? {
|
|
1251
|
+
level: l,
|
|
1252
|
+
primitiveIndex: f,
|
|
1253
|
+
screenCoverage: 0,
|
|
1254
|
+
screenspaceVolume: new A(),
|
|
1255
|
+
centrality: 1
|
|
1256
|
+
};
|
|
1257
|
+
h.level = l, h.primitiveIndex = f, h.screenCoverage = 0, h.screenspaceVolume.set(0, 0, 0), h.centrality = 1;
|
|
1258
|
+
let D = i.boundingBox ?? t.boundingBox;
|
|
1259
|
+
if (D || (t.computeBoundingBox(), D = t.boundingBox), !D) return h;
|
|
1260
|
+
if (me.copy(D).applyMatrix4(e), s.isPerspectiveCamera && Ft(me, r))
|
|
1261
|
+
return h.level = 0, h.screenCoverage = 1 / 0, h.screenspaceVolume.set(1 / 0, 1 / 0, 1 / 0), h;
|
|
1262
|
+
if (V.copy(me).applyMatrix4(r), a && s.isPerspectiveCamera && s.fov > 70) {
|
|
1263
|
+
const y = V.min, O = V.max;
|
|
1264
|
+
let I = y.x, P = y.y, _ = O.x, E = O.y;
|
|
1265
|
+
const C = 2, b = 1.5, m = (y.x + O.x) * 0.5, v = (y.y + O.y) * 0.5;
|
|
1266
|
+
I = (I - m) * C + m, P = (P - v) * C + v, _ = (_ - m) * C + m, E = (E - v) * C + v;
|
|
1267
|
+
const S = I < 0 && _ > 0 ? 0 : Math.min(Math.abs(y.x), Math.abs(O.x)), j = P < 0 && E > 0 ? 0 : Math.min(Math.abs(y.y), Math.abs(O.y)), J = Math.max(S, j);
|
|
1268
|
+
h.centrality = (b - J) * (b - J) * (b - J);
|
|
1269
|
+
}
|
|
1270
|
+
const w = V.getSize(Bt);
|
|
1271
|
+
w.multiplyScalar(0.5), globalThis.screen?.availHeight > 0 && o > 0 && w.multiplyScalar(o / globalThis.screen.availHeight), s.isPerspectiveCamera && (w.x *= s.aspect), Pe.copy(D).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);
|
|
1272
|
+
const x = Pe.getSize(qt), L = Math.max(w.x, w.y), M = Math.max(x.x, x.y);
|
|
1273
|
+
L !== 0 && M !== 0 && (w.z = x.z / M * L);
|
|
1274
|
+
const T = Math.max(w.x, w.y, w.z) * h.centrality;
|
|
1275
|
+
if (h.screenCoverage = T, h.screenspaceVolume.copy(w), T <= 0) return h;
|
|
1276
|
+
if (u) {
|
|
1277
|
+
const y = Wt.copy(r);
|
|
1278
|
+
y.invert(), F.copy(V.min), Z.copy(V.max), Z.x = F.x, ee.copy(V.max), ee.y = F.y, te.copy(V.max);
|
|
1279
|
+
const O = (F.z + te.z) * 0.5;
|
|
1280
|
+
F.z = Z.z = ee.z = te.z = O, F.applyMatrix4(y), Z.applyMatrix4(y), ee.applyMatrix4(y), te.applyMatrix4(y), u(F, Z, 255), u(F, ee, 255), u(Z, te, 255), u(ee, te, 255);
|
|
1281
|
+
}
|
|
1282
|
+
if (c?.length)
|
|
1283
|
+
for (let y = 0; y < c.length; y++) {
|
|
1284
|
+
const O = c[y], I = O.densities?.[f] || O.density || 1e-5;
|
|
1285
|
+
if (f > 0 && d && Ue() && !O.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] && (globalThis["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.")), I / T < n) {
|
|
1286
|
+
h.level = y;
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return h;
|
|
1291
|
+
}
|
|
1292
|
+
class G {
|
|
1002
1293
|
/**
|
|
1003
1294
|
* 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
1295
|
*/
|
|
1005
1296
|
static debugDrawLine;
|
|
1006
1297
|
/** @internal */
|
|
1007
1298
|
static getObjectLODState(t) {
|
|
1008
|
-
return t[
|
|
1299
|
+
return t[pe];
|
|
1009
1300
|
}
|
|
1010
1301
|
static addPlugin(t) {
|
|
1011
|
-
|
|
1302
|
+
U.push(t);
|
|
1012
1303
|
}
|
|
1013
1304
|
static removePlugin(t) {
|
|
1014
|
-
const e =
|
|
1015
|
-
e >= 0 &&
|
|
1305
|
+
const e = U.indexOf(t);
|
|
1306
|
+
e >= 0 && U.splice(e, 1);
|
|
1307
|
+
}
|
|
1308
|
+
/** Read-only snapshot of the currently registered plugins, for inspection. Use {@link addPlugin} / {@link removePlugin} to modify the registry. */
|
|
1309
|
+
static getPlugins() {
|
|
1310
|
+
return U;
|
|
1016
1311
|
}
|
|
1017
1312
|
/**
|
|
1018
1313
|
* Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
|
|
@@ -1020,20 +1315,20 @@ class v {
|
|
|
1020
1315
|
* @returns The LODsManager instance.
|
|
1021
1316
|
*/
|
|
1022
1317
|
static get(t, e) {
|
|
1023
|
-
if (t[
|
|
1024
|
-
return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[
|
|
1025
|
-
const s = new
|
|
1318
|
+
if (t[ge])
|
|
1319
|
+
return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[ge];
|
|
1320
|
+
const s = new G(t, {
|
|
1026
1321
|
engine: "unknown",
|
|
1027
1322
|
...e
|
|
1028
1323
|
});
|
|
1029
|
-
return t[
|
|
1324
|
+
return t[ge] = s, s;
|
|
1030
1325
|
}
|
|
1031
1326
|
renderer;
|
|
1032
1327
|
context;
|
|
1033
|
-
projectionScreenMatrix = new
|
|
1328
|
+
projectionScreenMatrix = new Ge();
|
|
1034
1329
|
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
1035
1330
|
get plugins() {
|
|
1036
|
-
return
|
|
1331
|
+
return U;
|
|
1037
1332
|
}
|
|
1038
1333
|
/**
|
|
1039
1334
|
* Force override the LOD level for all objects (meshes + textures) rendered in the scene
|
|
@@ -1071,45 +1366,99 @@ class v {
|
|
|
1071
1366
|
_newPromiseGroups = [];
|
|
1072
1367
|
_promiseGroupIds = 0;
|
|
1073
1368
|
/**
|
|
1074
|
-
*
|
|
1369
|
+
* Returns a promise that resolves once all LOD requests initiated during the next render cycles have finished loading.
|
|
1370
|
+
* This is useful for hiding low-resolution placeholders (e.g. with a loading overlay or CSS blur) until high-quality assets are ready.
|
|
1371
|
+
*
|
|
1372
|
+
* By default, the returned promise captures LOD loading requests for 2 frames and resolves when all of them complete.
|
|
1373
|
+
* Use `waitForFirstCapture` if no LOD requests may happen immediately (e.g. after a scene switch).
|
|
1374
|
+
*
|
|
1375
|
+
* @param opts - Optional configuration for how long to capture and what to wait for. See {@link PromiseGroupOptions}.
|
|
1376
|
+
* @returns A promise that resolves with `{ cancelled, awaited_count, resolved_count }` once all captured LOD loads complete (or the signal aborts).
|
|
1377
|
+
*
|
|
1378
|
+
* @example
|
|
1379
|
+
* ```ts
|
|
1380
|
+
* // Wait for initial LODs to finish loading, then remove a blur overlay
|
|
1381
|
+
* const result = await lodsManager.awaitLoading({
|
|
1382
|
+
* frames: 5,
|
|
1383
|
+
* signal: AbortSignal.timeout(10_000),
|
|
1384
|
+
* });
|
|
1385
|
+
* console.log(`Loaded ${result.resolved_count} of ${result.awaited_count} LODs`);
|
|
1386
|
+
* document.querySelector('.blur-overlay')?.remove();
|
|
1387
|
+
* ```
|
|
1388
|
+
*
|
|
1389
|
+
* @example
|
|
1390
|
+
* ```ts
|
|
1391
|
+
* // Wait until at least one LOD starts loading before resolving
|
|
1392
|
+
* await lodsManager.awaitLoading({ waitForFirstCapture: true });
|
|
1393
|
+
* ```
|
|
1075
1394
|
*/
|
|
1076
1395
|
awaitLoading(t) {
|
|
1077
|
-
const e = this._promiseGroupIds++, s = new
|
|
1396
|
+
const e = this._promiseGroupIds++, s = new le(this.#r, { ...t });
|
|
1078
1397
|
this._newPromiseGroups.push(s);
|
|
1079
1398
|
const r = performance.now();
|
|
1080
1399
|
return s.ready.finally(() => {
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1400
|
+
const n = this._newPromiseGroups.indexOf(s);
|
|
1401
|
+
n >= 0 && (this._newPromiseGroups.splice(n, 1), Ue() && performance.measure("LODsManager:awaitLoading", {
|
|
1083
1402
|
start: r,
|
|
1084
1403
|
detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
|
|
1085
1404
|
}));
|
|
1086
1405
|
}), s.ready;
|
|
1087
1406
|
}
|
|
1407
|
+
/** Track LOD work started outside this manager so {@link awaitLoading} waits for it too. */
|
|
1408
|
+
trackLoadingPromise(t, e, s) {
|
|
1409
|
+
return le.addPromise(t, e, s, this._newPromiseGroups), s;
|
|
1410
|
+
}
|
|
1088
1411
|
_postprocessPromiseGroups() {
|
|
1089
1412
|
if (this._newPromiseGroups.length !== 0)
|
|
1090
1413
|
for (let t = this._newPromiseGroups.length - 1; t >= 0; t--)
|
|
1091
1414
|
this._newPromiseGroups[t].update(this.#r);
|
|
1092
1415
|
}
|
|
1093
1416
|
_lodchangedlisteners = [];
|
|
1417
|
+
/**
|
|
1418
|
+
* Register a listener that is called whenever a mesh or texture LOD level has finished loading and has been applied.
|
|
1419
|
+
* The listener receives the type of asset (`"mesh"` or `"texture"`), the new LOD level, and the affected object.
|
|
1420
|
+
*
|
|
1421
|
+
* @param evt - The event type. Currently only `"changed"` is supported.
|
|
1422
|
+
* @param listener - Callback invoked after a LOD swap completes.
|
|
1423
|
+
* @return A function to unregister the listener.
|
|
1424
|
+
*
|
|
1425
|
+
* @example
|
|
1426
|
+
* ```ts
|
|
1427
|
+
* lodsManager.addEventListener("changed", ({ type, level, object }) => {
|
|
1428
|
+
* console.log(`${type} LOD changed to level ${level}`, object);
|
|
1429
|
+
* });
|
|
1430
|
+
* ```
|
|
1431
|
+
*/
|
|
1094
1432
|
addEventListener(t, e) {
|
|
1095
|
-
t === "changed"
|
|
1433
|
+
return t === "changed" ? (this._lodchangedlisteners.push(e), () => {
|
|
1434
|
+
this.removeEventListener(t, e);
|
|
1435
|
+
}) : () => {
|
|
1436
|
+
};
|
|
1096
1437
|
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Remove a previously registered `"changed"` event listener.
|
|
1440
|
+
* @param evt - The event type (`"changed"`).
|
|
1441
|
+
* @param listener - The listener to remove.
|
|
1442
|
+
* @return `true` if the listener was found and removed, `false` otherwise.
|
|
1443
|
+
*/
|
|
1097
1444
|
removeEventListener(t, e) {
|
|
1445
|
+
let s = !1;
|
|
1098
1446
|
if (t === "changed") {
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1447
|
+
const r = this._lodchangedlisteners.indexOf(e);
|
|
1448
|
+
r >= 0 && (this._lodchangedlisteners.splice(r, 1), s = !0);
|
|
1101
1449
|
}
|
|
1450
|
+
return s;
|
|
1102
1451
|
}
|
|
1103
1452
|
// readonly plugins: NEEDLE_progressive_plugin[] = [];
|
|
1104
1453
|
constructor(t, e) {
|
|
1105
1454
|
this.renderer = t, this.context = { ...e };
|
|
1106
1455
|
}
|
|
1107
1456
|
#t;
|
|
1108
|
-
#n = new Ze();
|
|
1109
1457
|
#r = 0;
|
|
1458
|
+
#n = 0;
|
|
1110
1459
|
#o = 0;
|
|
1111
|
-
#i = 0;
|
|
1112
1460
|
#s = 0;
|
|
1461
|
+
#i = Gt();
|
|
1113
1462
|
_fpsBuffer = [60, 60, 60, 60, 60];
|
|
1114
1463
|
/**
|
|
1115
1464
|
* Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
|
|
@@ -1120,29 +1469,46 @@ class v {
|
|
|
1120
1469
|
let t = 0;
|
|
1121
1470
|
this.#t = this.renderer.render;
|
|
1122
1471
|
const e = this;
|
|
1123
|
-
|
|
1124
|
-
const
|
|
1125
|
-
(
|
|
1126
|
-
const
|
|
1127
|
-
e.#t.call(this, s, r), e.onAfterRender(s, r,
|
|
1472
|
+
_e(this.renderer), this.renderer.render = function(s, r) {
|
|
1473
|
+
const n = e.renderer.getRenderTarget();
|
|
1474
|
+
(n == null || "isXRRenderTarget" in n && n.isXRRenderTarget) && (t = 0, e.#r += 1, e.#i.update?.(), e.#n = Math.max(e.#i.getDelta(), 1 / 1e3), e.#o += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length, $ && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
|
|
1475
|
+
const o = t++;
|
|
1476
|
+
e.#t.call(this, s, r), e.onAfterRender(s, r, o);
|
|
1128
1477
|
};
|
|
1129
1478
|
}
|
|
1130
1479
|
disable() {
|
|
1131
1480
|
this.#t && (console.debug("[gltf-progressive] Disabling LODsManager for renderer"), this.renderer.render = this.#t, this.#t = void 0);
|
|
1132
1481
|
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Manually trigger a LOD update for a scene and camera.
|
|
1484
|
+
* Only needed when {@link manual} is set to `true` — otherwise LOD updates happen automatically on each render call.
|
|
1485
|
+
*
|
|
1486
|
+
* @param scene - The scene containing objects with progressive LODs.
|
|
1487
|
+
* @param camera - The camera used to determine screen coverage and LOD levels.
|
|
1488
|
+
*
|
|
1489
|
+
* @example
|
|
1490
|
+
* ```ts
|
|
1491
|
+
* const lodsManager = LODsManager.get(renderer);
|
|
1492
|
+
* lodsManager.manual = true;
|
|
1493
|
+
* // ... later, trigger an update at a specific point:
|
|
1494
|
+
* lodsManager.update(scene, camera);
|
|
1495
|
+
* ```
|
|
1496
|
+
*/
|
|
1133
1497
|
update(t, e) {
|
|
1134
1498
|
this.internalUpdate(t, e);
|
|
1135
1499
|
}
|
|
1136
1500
|
onAfterRender(t, e, s) {
|
|
1137
1501
|
if (this.pause) return;
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1502
|
+
const r = this.getRenderList(t, e, s);
|
|
1503
|
+
if (!r) return;
|
|
1504
|
+
const n = r.opaque;
|
|
1505
|
+
let o = !0;
|
|
1506
|
+
if (n.length === 1) {
|
|
1507
|
+
const l = n[0].material;
|
|
1508
|
+
(l.name === "EffectMaterial" || l.name === "CopyShader") && (o = !1);
|
|
1143
1509
|
}
|
|
1144
|
-
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (
|
|
1145
|
-
if (
|
|
1510
|
+
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (o = !1), o) {
|
|
1511
|
+
if (It || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, $ && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, $ && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
|
|
1146
1512
|
return;
|
|
1147
1513
|
this.internalUpdate(t, e), this._postprocessPromiseGroups();
|
|
1148
1514
|
}
|
|
@@ -1151,12 +1517,14 @@ class v {
|
|
|
1151
1517
|
* Update LODs in a scene
|
|
1152
1518
|
*/
|
|
1153
1519
|
internalUpdate(t, e) {
|
|
1154
|
-
const s = this.
|
|
1520
|
+
const s = this.getRenderList(t, e, 0);
|
|
1521
|
+
if (!s) return;
|
|
1522
|
+
const r = s.opaque;
|
|
1155
1523
|
this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix, e.matrixWorldInverse);
|
|
1156
|
-
const
|
|
1524
|
+
const n = this.targetTriangleDensity;
|
|
1157
1525
|
for (const a of r) {
|
|
1158
1526
|
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
|
-
|
|
1527
|
+
$ && (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
1528
|
continue;
|
|
1161
1529
|
}
|
|
1162
1530
|
switch (a.material.type) {
|
|
@@ -1168,38 +1536,52 @@ class v {
|
|
|
1168
1536
|
case "MeshDepthMaterial":
|
|
1169
1537
|
continue;
|
|
1170
1538
|
}
|
|
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
1539
|
const u = a.object;
|
|
1177
|
-
(u instanceof
|
|
1540
|
+
(u instanceof Y || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1178
1541
|
}
|
|
1179
|
-
const
|
|
1180
|
-
for (const a of
|
|
1542
|
+
const o = s.transparent;
|
|
1543
|
+
for (const a of o) {
|
|
1181
1544
|
const u = a.object;
|
|
1182
|
-
(u instanceof
|
|
1545
|
+
(u instanceof Y || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1183
1546
|
}
|
|
1184
1547
|
const l = s.transmissive;
|
|
1185
1548
|
for (const a of l) {
|
|
1186
1549
|
const u = a.object;
|
|
1187
|
-
(u instanceof
|
|
1550
|
+
(u instanceof Y || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1188
1551
|
}
|
|
1189
1552
|
}
|
|
1553
|
+
getRenderList(t, e, s) {
|
|
1554
|
+
const r = this.renderer;
|
|
1555
|
+
let n = null;
|
|
1556
|
+
if (r.isWebGPURenderer === !0) {
|
|
1557
|
+
const o = r._renderLists;
|
|
1558
|
+
if (!o) return null;
|
|
1559
|
+
n = o.get(t, e);
|
|
1560
|
+
} else if (r.isWebGLRenderer === !0) {
|
|
1561
|
+
const o = r.renderLists;
|
|
1562
|
+
if (!o) return null;
|
|
1563
|
+
n = o.get(t, s);
|
|
1564
|
+
}
|
|
1565
|
+
return n ? {
|
|
1566
|
+
opaque: n.opaque || [],
|
|
1567
|
+
transparent: n.transparent || [],
|
|
1568
|
+
transmissive: n.transmissive || n.transparentDoublePass || [],
|
|
1569
|
+
transparentDoublePass: n.transparentDoublePass || []
|
|
1570
|
+
} : null;
|
|
1571
|
+
}
|
|
1190
1572
|
/** Update the LOD levels for the renderer. */
|
|
1191
1573
|
updateLODs(t, e, s, r) {
|
|
1192
1574
|
s.userData || (s.userData = {});
|
|
1193
|
-
let
|
|
1194
|
-
if (
|
|
1575
|
+
let n = s[pe];
|
|
1576
|
+
if (n || (n = new zt(), s[pe] = n), n.frames++ < 2)
|
|
1195
1577
|
return;
|
|
1196
|
-
for (const l of
|
|
1578
|
+
for (const l of U)
|
|
1197
1579
|
l.onBeforeUpdateLOD?.(this.renderer, t, e, s);
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
for (const l of
|
|
1201
|
-
l.onAfterUpdatedLOD?.(this.renderer, t, e, s,
|
|
1202
|
-
|
|
1580
|
+
const o = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : K;
|
|
1581
|
+
o >= 0 ? (R.mesh_lod = o, R.texture_lod = o) : (this.calculateLodLevel(e, s, n, r, R), R.mesh_lod = Math.round(R.mesh_lod), R.texture_lod = Math.round(R.texture_lod)), R.mesh_lod >= 0 && this.loadProgressiveMeshes(s, R.mesh_lod), s.material && R.texture_lod >= 0 && this.loadProgressiveTextures(s.material, R.texture_lod, o), g && s.material && !s.isGizmo && ze(s.material), At && s.material && !s.isGizmo && !s.isBatchedMesh && Ee(s.material, R.mesh_lod);
|
|
1582
|
+
for (const l of U)
|
|
1583
|
+
l.onAfterUpdatedLOD?.(this.renderer, t, e, s, R);
|
|
1584
|
+
n.lastLodLevel_Mesh = R.mesh_lod, n.lastLodLevel_Texture = R.texture_lod;
|
|
1203
1585
|
}
|
|
1204
1586
|
/** Load progressive textures for the given material
|
|
1205
1587
|
* @param material the material to load the textures for
|
|
@@ -1210,16 +1592,18 @@ class v {
|
|
|
1210
1592
|
if (!t) return;
|
|
1211
1593
|
if (Array.isArray(t)) {
|
|
1212
1594
|
for (const o of t)
|
|
1213
|
-
this.loadProgressiveTextures(o, e);
|
|
1595
|
+
this.loadProgressiveTextures(o, e, s);
|
|
1214
1596
|
return;
|
|
1215
1597
|
}
|
|
1216
1598
|
let r = !1;
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1599
|
+
(t[N] === void 0 || e < t[N]) && (r = !0);
|
|
1600
|
+
const n = s !== void 0 && s >= 0;
|
|
1601
|
+
if (n && (r = t[N] != s, e = s), r) {
|
|
1602
|
+
t[N] = e;
|
|
1603
|
+
const o = n ? { force: !0 } : void 0, l = p.assignTextureLOD(t, e, o).then((a) => {
|
|
1604
|
+
this._lodchangedlisteners.forEach((u) => u({ type: "texture", level: e, object: t }));
|
|
1221
1605
|
});
|
|
1222
|
-
|
|
1606
|
+
le.addPromise("texture", t, l, this._newPromiseGroups);
|
|
1223
1607
|
}
|
|
1224
1608
|
}
|
|
1225
1609
|
/** Load progressive meshes for the given mesh
|
|
@@ -1230,143 +1614,110 @@ class v {
|
|
|
1230
1614
|
*/
|
|
1231
1615
|
loadProgressiveMeshes(t, e) {
|
|
1232
1616
|
if (!t) return Promise.resolve(null);
|
|
1233
|
-
let s = t[
|
|
1617
|
+
let s = t[N] !== e;
|
|
1234
1618
|
const r = t["DEBUG:LOD"];
|
|
1235
|
-
if (r != null && (s = t[
|
|
1236
|
-
t[
|
|
1237
|
-
const
|
|
1238
|
-
return
|
|
1619
|
+
if (r != null && (s = t[N] != r, e = r), s) {
|
|
1620
|
+
t[N] = e;
|
|
1621
|
+
const n = t.geometry, o = p.assignMeshLOD(t, e).then((l) => (l && t[N] == e && n != t.geometry && this._lodchangedlisteners.forEach((a) => a({ type: "mesh", level: e, object: t })), l));
|
|
1622
|
+
return le.addPromise("mesh", t, o, this._newPromiseGroups), o;
|
|
1239
1623
|
}
|
|
1240
1624
|
return Promise.resolve(null);
|
|
1241
1625
|
}
|
|
1242
1626
|
// private testIfLODLevelsAreAvailable() {
|
|
1243
|
-
_sphere = new
|
|
1244
|
-
_tempBox = new ge();
|
|
1245
|
-
_tempBox2 = new ge();
|
|
1246
|
-
tempMatrix = new Me();
|
|
1627
|
+
_sphere = new Ie();
|
|
1247
1628
|
_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
1629
|
static skinnedMeshBoundsFrameOffsetCounter = 0;
|
|
1260
1630
|
static $skinnedMeshBoundsOffset = /* @__PURE__ */ Symbol("gltf-progressive-skinnedMeshBoundsOffset");
|
|
1261
1631
|
// #region calculateLodLevel
|
|
1262
|
-
calculateLodLevel(t, e, s, r,
|
|
1632
|
+
calculateLodLevel(t, e, s, r, n) {
|
|
1263
1633
|
if (!e) {
|
|
1264
|
-
|
|
1634
|
+
n.mesh_lod = -1, n.texture_lod = -1;
|
|
1265
1635
|
return;
|
|
1266
1636
|
}
|
|
1267
1637
|
if (!t) {
|
|
1268
|
-
|
|
1638
|
+
n.mesh_lod = -1, n.texture_lod = -1;
|
|
1269
1639
|
return;
|
|
1270
1640
|
}
|
|
1271
1641
|
let l = 10 + 1, a = !1;
|
|
1272
|
-
if (
|
|
1642
|
+
if ($ && e["DEBUG:LOD"] != null)
|
|
1273
1643
|
return e["DEBUG:LOD"];
|
|
1274
|
-
const u =
|
|
1275
|
-
if (!
|
|
1276
|
-
|
|
1644
|
+
const u = p.getMeshLODExtension(e.geometry)?.lods, d = p.getPrimitiveIndex(e.geometry), c = u && u.length > 0, f = p.getMaterialMinMaxLODsCount(e.material), h = f.min_count !== 1 / 0 && f.min_count >= 0 && f.max_count >= 0;
|
|
1645
|
+
if (!c && !h) {
|
|
1646
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1277
1647
|
return;
|
|
1278
1648
|
}
|
|
1279
|
-
|
|
1280
|
-
const
|
|
1281
|
-
let
|
|
1649
|
+
c || (a = !0, l = 0);
|
|
1650
|
+
const D = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
|
|
1651
|
+
let w = e.geometry.boundingBox;
|
|
1282
1652
|
if (e.type === "SkinnedMesh") {
|
|
1283
|
-
const
|
|
1284
|
-
if (!
|
|
1285
|
-
|
|
1653
|
+
const x = e;
|
|
1654
|
+
if (!x.boundingBox)
|
|
1655
|
+
x.computeBoundingBox();
|
|
1286
1656
|
else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
|
|
1287
|
-
if (!
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1657
|
+
if (!x[G.$skinnedMeshBoundsOffset]) {
|
|
1658
|
+
const M = G.skinnedMeshBoundsFrameOffsetCounter++;
|
|
1659
|
+
x[G.$skinnedMeshBoundsOffset] = M;
|
|
1290
1660
|
}
|
|
1291
|
-
const
|
|
1292
|
-
if ((s.frames +
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1661
|
+
const L = x[G.$skinnedMeshBoundsOffset];
|
|
1662
|
+
if ((s.frames + L) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
|
|
1663
|
+
const M = ae(x), T = x.geometry;
|
|
1664
|
+
M && (x.geometry = M), x.computeBoundingBox(), x.geometry = T;
|
|
1295
1665
|
}
|
|
1296
1666
|
}
|
|
1297
|
-
|
|
1667
|
+
w = x.boundingBox;
|
|
1298
1668
|
}
|
|
1299
|
-
if (
|
|
1300
|
-
const d = t;
|
|
1669
|
+
if (w) {
|
|
1301
1670
|
if (e.geometry.attributes.color && e.geometry.attributes.color.count < 100 && e.geometry.boundingSphere) {
|
|
1302
1671
|
this._sphere.copy(e.geometry.boundingSphere), this._sphere.applyMatrix4(e.matrixWorld);
|
|
1303
|
-
const
|
|
1304
|
-
if (this._sphere.containsPoint(
|
|
1305
|
-
|
|
1672
|
+
const M = t.getWorldPosition(this._tempWorldPosition);
|
|
1673
|
+
if (this._sphere.containsPoint(M)) {
|
|
1674
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1306
1675
|
return;
|
|
1307
1676
|
}
|
|
1308
1677
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1678
|
+
const x = Ut({
|
|
1679
|
+
geometry: e.geometry,
|
|
1680
|
+
matrixWorld: e.matrixWorld,
|
|
1681
|
+
camera: t,
|
|
1682
|
+
projectionScreenMatrix: this.projectionScreenMatrix,
|
|
1683
|
+
desiredDensity: r,
|
|
1684
|
+
canvasHeight: D,
|
|
1685
|
+
currentLevel: s.lastLodLevel_Mesh,
|
|
1686
|
+
boundingBox: w,
|
|
1687
|
+
xrEnabled: this.renderer.xr.enabled,
|
|
1688
|
+
debugDrawLine: $ ? G.debugDrawLine : void 0,
|
|
1689
|
+
warnMissingPrimitiveDensities: !0
|
|
1690
|
+
});
|
|
1691
|
+
if (s.lastCentrality = x.centrality, s.lastScreenCoverage = x.screenCoverage, s.lastScreenspaceVolume.copy(x.screenspaceVolume), x.screenCoverage === 1 / 0) {
|
|
1692
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1311
1693
|
return;
|
|
1312
1694
|
}
|
|
1313
|
-
|
|
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);
|
|
1695
|
+
x.level >= 0 && x.level < l && (l = x.level, a = !0);
|
|
1345
1696
|
}
|
|
1346
|
-
if (a ?
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1697
|
+
if (a ? n.mesh_lod = l : n.mesh_lod = s.lastLodLevel_Mesh, $ && n.mesh_lod != s.lastLodLevel_Mesh) {
|
|
1698
|
+
const L = u?.[n.mesh_lod];
|
|
1699
|
+
L && console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${L.densities?.[d].toFixed(0)}) | ${e.name}`);
|
|
1349
1700
|
}
|
|
1350
|
-
if (
|
|
1351
|
-
const
|
|
1701
|
+
if (h) {
|
|
1702
|
+
const x = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1352
1703
|
if (s.lastLodLevel_Texture < 0) {
|
|
1353
|
-
if (
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1704
|
+
if (n.texture_lod = f.max_count - 1, $) {
|
|
1705
|
+
const L = f.lods[f.max_count - 1];
|
|
1706
|
+
$ && console.log(`First Texture LOD ${n.texture_lod} (${L.max_height}px) - ${e.name}`);
|
|
1356
1707
|
}
|
|
1357
1708
|
} else {
|
|
1358
|
-
const
|
|
1359
|
-
let
|
|
1360
|
-
this.context?.engine === "model-viewer" && (
|
|
1361
|
-
const
|
|
1362
|
-
let
|
|
1363
|
-
for (let
|
|
1364
|
-
const
|
|
1365
|
-
if (!(
|
|
1366
|
-
if (
|
|
1367
|
-
const
|
|
1368
|
-
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${
|
|
1369
|
-
Screensize: ${
|
|
1709
|
+
const L = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
|
|
1710
|
+
let M = s.lastScreenCoverage * 4;
|
|
1711
|
+
this.context?.engine === "model-viewer" && (M *= 1.5);
|
|
1712
|
+
const T = this.renderer.getPixelRatio?.() || globalThis.devicePixelRatio || 1, O = D / T * M;
|
|
1713
|
+
let I = !1;
|
|
1714
|
+
for (let P = f.lods.length - 1; P >= 0; P--) {
|
|
1715
|
+
const _ = f.lods[P];
|
|
1716
|
+
if (!(x && _.max_height >= 2048) && !(be() && _.max_height > 4096) && (_.max_height > O || !I && P === 0)) {
|
|
1717
|
+
if (I = !0, n.texture_lod = P, $ && n.texture_lod < s.lastLodLevel_Texture) {
|
|
1718
|
+
const E = _.max_height;
|
|
1719
|
+
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${E}px
|
|
1720
|
+
Screensize: ${O.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${L.toFixed(1)}
|
|
1370
1721
|
${e.name}`);
|
|
1371
1722
|
}
|
|
1372
1723
|
break;
|
|
@@ -1374,10 +1725,10 @@ ${e.name}`);
|
|
|
1374
1725
|
}
|
|
1375
1726
|
}
|
|
1376
1727
|
} else
|
|
1377
|
-
|
|
1728
|
+
n.texture_lod = 0;
|
|
1378
1729
|
}
|
|
1379
1730
|
}
|
|
1380
|
-
class
|
|
1731
|
+
class zt {
|
|
1381
1732
|
frames = 0;
|
|
1382
1733
|
lastLodLevel_Mesh = -1;
|
|
1383
1734
|
lastLodLevel_Texture = -1;
|
|
@@ -1385,50 +1736,65 @@ class Tt {
|
|
|
1385
1736
|
lastScreenspaceVolume = new A();
|
|
1386
1737
|
lastCentrality = 0;
|
|
1387
1738
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1739
|
+
function Ee(i, t) {
|
|
1740
|
+
if (!(t < 0)) {
|
|
1741
|
+
if (Array.isArray(i)) {
|
|
1742
|
+
for (const e of i)
|
|
1743
|
+
Ee(e, t);
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
"color" in i && i.color instanceof $e && (i.color.copy(Et(t, $t)), i.needsUpdate = !0);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
function Et(i, t) {
|
|
1750
|
+
const e = Math.max(0, Math.min(Re.length - 1, Math.floor(i)));
|
|
1751
|
+
return t.setHex(Re[e]);
|
|
1752
|
+
}
|
|
1753
|
+
const Ce = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ue = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
|
|
1754
|
+
let ye = null;
|
|
1755
|
+
function Ne() {
|
|
1756
|
+
const i = Nt();
|
|
1392
1757
|
i && (i.mapURLs(function(t) {
|
|
1393
|
-
return
|
|
1394
|
-
}),
|
|
1758
|
+
return ke(), t;
|
|
1759
|
+
}), ke(), ye?.disconnect(), ye = new MutationObserver((t) => {
|
|
1395
1760
|
t.forEach((e) => {
|
|
1396
1761
|
e.addedNodes.forEach((s) => {
|
|
1397
|
-
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" &&
|
|
1762
|
+
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ve(s);
|
|
1398
1763
|
});
|
|
1399
1764
|
});
|
|
1400
|
-
}),
|
|
1765
|
+
}), ye.observe(document, { childList: !0, subtree: !0 }));
|
|
1401
1766
|
}
|
|
1402
|
-
function
|
|
1767
|
+
function Nt() {
|
|
1403
1768
|
if (typeof customElements > "u") return null;
|
|
1404
1769
|
const i = customElements.get("model-viewer");
|
|
1405
1770
|
return i || (customElements.whenDefined("model-viewer").then(() => {
|
|
1406
|
-
console.debug("[gltf-progressive] model-viewer defined"),
|
|
1771
|
+
console.debug("[gltf-progressive] model-viewer defined"), Ne();
|
|
1407
1772
|
}), null);
|
|
1408
1773
|
}
|
|
1409
|
-
function
|
|
1774
|
+
function ke() {
|
|
1410
1775
|
if (typeof document > "u") return;
|
|
1411
1776
|
document.querySelectorAll("model-viewer").forEach((t) => {
|
|
1412
|
-
|
|
1777
|
+
Ve(t);
|
|
1413
1778
|
});
|
|
1414
1779
|
}
|
|
1415
|
-
const
|
|
1416
|
-
let
|
|
1417
|
-
function
|
|
1418
|
-
if (!i ||
|
|
1780
|
+
const Ae = /* @__PURE__ */ new WeakSet();
|
|
1781
|
+
let Vt = 0;
|
|
1782
|
+
function Ve(i) {
|
|
1783
|
+
if (!i || Ae.has(i))
|
|
1419
1784
|
return null;
|
|
1420
|
-
|
|
1785
|
+
Ae.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++Vt + `
|
|
1421
1786
|
`, i.getAttribute("src"));
|
|
1422
1787
|
let t = null, e = null, s = null;
|
|
1423
1788
|
for (let r = i; r != null; r = Object.getPrototypeOf(r)) {
|
|
1424
|
-
const
|
|
1425
|
-
!t &&
|
|
1789
|
+
const n = Object.getOwnPropertySymbols(r), o = n.find((u) => u.toString() == "Symbol(renderer)"), l = n.find((u) => u.toString() == "Symbol(scene)"), a = n.find((u) => u.toString() == "Symbol(needsRender)");
|
|
1790
|
+
!t && o != null && (t = i[o].threeRenderer), !e && l != null && (e = i[l]), !s && a != null && (s = i[a]);
|
|
1426
1791
|
}
|
|
1427
1792
|
if (t && e) {
|
|
1428
1793
|
let r = function() {
|
|
1429
1794
|
if (s) {
|
|
1430
|
-
let
|
|
1431
|
-
|
|
1795
|
+
let o = 0;
|
|
1796
|
+
const l = setInterval(() => {
|
|
1797
|
+
if (o++ > 5) {
|
|
1432
1798
|
clearInterval(l);
|
|
1433
1799
|
return;
|
|
1434
1800
|
}
|
|
@@ -1437,20 +1803,20 @@ function Ge(i) {
|
|
|
1437
1803
|
}
|
|
1438
1804
|
};
|
|
1439
1805
|
console.debug("[gltf-progressive] setup model-viewer");
|
|
1440
|
-
const
|
|
1441
|
-
return
|
|
1806
|
+
const n = G.get(t, { engine: "model-viewer" });
|
|
1807
|
+
return G.addPlugin(new Xt()), n.enable(), n.addEventListener("changed", () => {
|
|
1442
1808
|
s?.call(i);
|
|
1443
|
-
}), i.addEventListener("model-visibility", (
|
|
1444
|
-
|
|
1809
|
+
}), i.addEventListener("model-visibility", (o) => {
|
|
1810
|
+
o.detail.visible && s?.call(i);
|
|
1445
1811
|
}), i.addEventListener("load", () => {
|
|
1446
1812
|
r();
|
|
1447
1813
|
}), () => {
|
|
1448
|
-
|
|
1814
|
+
n.disable();
|
|
1449
1815
|
};
|
|
1450
1816
|
}
|
|
1451
1817
|
return null;
|
|
1452
1818
|
}
|
|
1453
|
-
class
|
|
1819
|
+
class Xt {
|
|
1454
1820
|
_didWarnAboutMissingUrl = !1;
|
|
1455
1821
|
onBeforeUpdateLOD(t, e, s, r) {
|
|
1456
1822
|
this.tryParseMeshLOD(e, r), this.tryParseTextureLOD(e, r);
|
|
@@ -1468,50 +1834,50 @@ class At {
|
|
|
1468
1834
|
return t.element;
|
|
1469
1835
|
}
|
|
1470
1836
|
tryParseTextureLOD(t, e) {
|
|
1471
|
-
if (e[
|
|
1472
|
-
e[
|
|
1473
|
-
const s = this.tryGetCurrentGLTF(t), r = this.tryGetCurrentModelViewer(t),
|
|
1474
|
-
if (
|
|
1475
|
-
let
|
|
1476
|
-
if (a[
|
|
1477
|
-
a[
|
|
1837
|
+
if (e[ue] == !0) return;
|
|
1838
|
+
e[ue] = !0;
|
|
1839
|
+
const s = this.tryGetCurrentGLTF(t), r = this.tryGetCurrentModelViewer(t), n = this.getUrl(r);
|
|
1840
|
+
if (n && s && e.material) {
|
|
1841
|
+
let o = function(a) {
|
|
1842
|
+
if (a[ue] == !0) return;
|
|
1843
|
+
a[ue] = !0, a.userData && (a.userData.LOD = -1);
|
|
1478
1844
|
const u = Object.keys(a);
|
|
1479
|
-
for (let
|
|
1480
|
-
const
|
|
1481
|
-
if (
|
|
1482
|
-
const
|
|
1483
|
-
if (
|
|
1484
|
-
const
|
|
1485
|
-
if (!
|
|
1486
|
-
console.warn("Texture data not found for texture index " +
|
|
1845
|
+
for (let d = 0; d < u.length; d++) {
|
|
1846
|
+
const c = u[d], f = a[c];
|
|
1847
|
+
if (f?.isTexture === !0) {
|
|
1848
|
+
const h = f.userData?.associations?.textures;
|
|
1849
|
+
if (h == null) continue;
|
|
1850
|
+
const D = s.parser.json.textures[h];
|
|
1851
|
+
if (!D) {
|
|
1852
|
+
console.warn("Texture data not found for texture index " + h);
|
|
1487
1853
|
continue;
|
|
1488
1854
|
}
|
|
1489
|
-
if (
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1855
|
+
if (D?.extensions?.[z]) {
|
|
1856
|
+
const w = D.extensions[z];
|
|
1857
|
+
w && n && p.registerTexture(n, f, w.lods.length, h, w);
|
|
1492
1858
|
}
|
|
1493
1859
|
}
|
|
1494
1860
|
}
|
|
1495
1861
|
};
|
|
1496
1862
|
const l = e.material;
|
|
1497
|
-
if (Array.isArray(l)) for (const a of l)
|
|
1498
|
-
else
|
|
1863
|
+
if (Array.isArray(l)) for (const a of l) o(a);
|
|
1864
|
+
else o(l);
|
|
1499
1865
|
}
|
|
1500
1866
|
}
|
|
1501
1867
|
tryParseMeshLOD(t, e) {
|
|
1502
|
-
if (e[
|
|
1503
|
-
e[
|
|
1868
|
+
if (e[Ce] == !0) return;
|
|
1869
|
+
e[Ce] = !0;
|
|
1504
1870
|
const s = this.tryGetCurrentModelViewer(t), r = this.getUrl(s);
|
|
1505
1871
|
if (!r)
|
|
1506
1872
|
return;
|
|
1507
|
-
const
|
|
1508
|
-
if (
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1873
|
+
const n = e.userData?.gltfExtensions?.[z];
|
|
1874
|
+
if (n && r) {
|
|
1875
|
+
const o = e.uuid;
|
|
1876
|
+
p.registerMesh(r, o, e, 0, n.lods.length, n);
|
|
1511
1877
|
}
|
|
1512
1878
|
}
|
|
1513
1879
|
}
|
|
1514
|
-
function
|
|
1880
|
+
function jt(...i) {
|
|
1515
1881
|
let t, e, s, r;
|
|
1516
1882
|
switch (i.length) {
|
|
1517
1883
|
case 2:
|
|
@@ -1526,22 +1892,22 @@ function Rt(...i) {
|
|
|
1526
1892
|
default:
|
|
1527
1893
|
throw new Error("Invalid arguments");
|
|
1528
1894
|
}
|
|
1529
|
-
|
|
1895
|
+
_e(e), qe(s), Fe(s, {
|
|
1530
1896
|
progressive: !0,
|
|
1531
1897
|
...r?.hints
|
|
1532
|
-
}), s.register((
|
|
1533
|
-
const
|
|
1534
|
-
return r?.enableLODsManager !== !1 &&
|
|
1898
|
+
}), s.register((o) => new p(o));
|
|
1899
|
+
const n = G.get(e);
|
|
1900
|
+
return r?.enableLODsManager !== !1 && n.enable(), n;
|
|
1535
1901
|
}
|
|
1536
|
-
|
|
1537
|
-
if (!
|
|
1902
|
+
Ne();
|
|
1903
|
+
if (!Mt) {
|
|
1538
1904
|
const i = {
|
|
1539
1905
|
gltfProgressive: {
|
|
1540
|
-
useNeedleProgressive:
|
|
1541
|
-
LODsManager:
|
|
1542
|
-
configureLoader:
|
|
1543
|
-
getRaycastMesh:
|
|
1544
|
-
useRaycastMeshes:
|
|
1906
|
+
useNeedleProgressive: jt,
|
|
1907
|
+
LODsManager: G,
|
|
1908
|
+
configureLoader: Fe,
|
|
1909
|
+
getRaycastMesh: ae,
|
|
1910
|
+
useRaycastMeshes: bt
|
|
1545
1911
|
}
|
|
1546
1912
|
};
|
|
1547
1913
|
if (!globalThis.Needle)
|
|
@@ -1551,18 +1917,21 @@ if (!yt) {
|
|
|
1551
1917
|
globalThis.Needle[t] = i[t];
|
|
1552
1918
|
}
|
|
1553
1919
|
export {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1920
|
+
z as EXTENSION_NAME,
|
|
1921
|
+
G as LODsManager,
|
|
1922
|
+
p as NEEDLE_progressive,
|
|
1923
|
+
ut as VERSION,
|
|
1924
|
+
qe as addDracoAndKTX2Loaders,
|
|
1925
|
+
Ut as calculateMeshLODLevel,
|
|
1926
|
+
Fe as configureLoader,
|
|
1927
|
+
_e as createLoaders,
|
|
1928
|
+
Et as getLODColor,
|
|
1929
|
+
ae as getRaycastMesh,
|
|
1930
|
+
Re as lodDebugColors,
|
|
1931
|
+
Ne as patchModelViewer,
|
|
1932
|
+
_t as registerRaycastMesh,
|
|
1933
|
+
ht as setDracoDecoderLocation,
|
|
1934
|
+
gt as setKTX2TranscoderLocation,
|
|
1935
|
+
jt as useNeedleProgressive,
|
|
1936
|
+
bt as useRaycastMeshes
|
|
1568
1937
|
};
|