@needle-tools/gltf-progressive 4.0.0-alpha.1 → 4.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- 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 +1025 -669
- package/gltf-progressive.min.js +9 -9
- package/gltf-progressive.umd.cjs +9 -9
- package/lib/extension.d.ts +73 -7
- package/lib/extension.js +351 -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 j, Mesh as K, Box3 as ue, Vector3 as k, Sphere as Ae, CompressedTexture as et, Texture as q, Matrix3 as tt, InterleavedBuffer as st, InterleavedBufferAttribute as rt, BufferAttribute as nt, TextureLoader as it, Color as Ie, Matrix4 as $e } from "three";
|
|
3
|
+
import { GLTFLoader as De } 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 $ = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", H = "https://cdn.needle.tools/static/three/0.179.1/basis2/";
|
|
11
|
+
const ct = $, ft = H, Be = new URL($ + "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 ${$} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), $ === ct && ht("./include/draco/"), H === ft && gt("./include/ktx2/");
|
|
19
20
|
}).finally(() => {
|
|
20
|
-
|
|
21
|
+
qe();
|
|
21
22
|
});
|
|
22
|
-
const
|
|
23
|
-
dracoDecoderPath:
|
|
24
|
-
ktx2TranscoderPath:
|
|
23
|
+
const dt = () => ({
|
|
24
|
+
dracoDecoderPath: $,
|
|
25
|
+
ktx2TranscoderPath: H
|
|
25
26
|
});
|
|
26
|
-
function
|
|
27
|
-
|
|
27
|
+
function ht(i) {
|
|
28
|
+
$ = i, C && C[ye] != $ ? (console.debug("Updating Draco decoder path to " + i), C[ye] = $, C.setDecoderPath($), C.preload()) : console.debug("Setting Draco decoder path to " + i);
|
|
28
29
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
30
|
+
function gt(i) {
|
|
31
|
+
H = i, B && B.transcoderPath != H ? (console.debug("Updating KTX2 transcoder path to " + i), B.setTranscoderPath(H), B.init()) : console.debug("Setting KTX2 transcoder path to " + i);
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
return
|
|
33
|
+
function _e(i) {
|
|
34
|
+
return qe(), i ? B.detectSupport(i) : i !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: C, ktx2Loader: B, meshoptDecoder: le };
|
|
34
35
|
}
|
|
35
|
-
function
|
|
36
|
-
i.dracoLoader || i.setDRACOLoader(C), i.ktx2Loader || i.setKTX2Loader(
|
|
36
|
+
function Ge(i) {
|
|
37
|
+
i.dracoLoader || i.setDRACOLoader(C), i.ktx2Loader || i.setKTX2Loader(B), i.meshoptDecoder || i.setMeshoptDecoder(le);
|
|
37
38
|
}
|
|
38
|
-
const
|
|
39
|
-
let C,
|
|
40
|
-
function
|
|
41
|
-
C || (C = new
|
|
39
|
+
const ye = /* @__PURE__ */ Symbol("dracoDecoderPath");
|
|
40
|
+
let C, le, B;
|
|
41
|
+
function qe() {
|
|
42
|
+
C || (C = new at(), C[ye] = $, C.setDecoderPath($), C.preload()), B || (B = new lt(), B.setTranscoderPath(H), B.init()), le || (le = ot);
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
-
function
|
|
45
|
-
let e =
|
|
46
|
-
e ? e = Object.assign(e, t) : e = t,
|
|
44
|
+
const xe = /* @__PURE__ */ new WeakMap();
|
|
45
|
+
function We(i, t) {
|
|
46
|
+
let e = xe.get(i);
|
|
47
|
+
e ? e = Object.assign(e, t) : e = t, xe.set(i, e);
|
|
47
48
|
}
|
|
48
|
-
const
|
|
49
|
-
function
|
|
50
|
-
const t =
|
|
49
|
+
const pt = De.prototype.load;
|
|
50
|
+
function mt(...i) {
|
|
51
|
+
const t = xe.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
|
+
De.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 Te(i) {
|
|
61
72
|
return i != null && i.data != null;
|
|
62
73
|
}
|
|
63
|
-
function
|
|
74
|
+
function we(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 Le(i) {
|
|
83
|
+
const t = xt(i), e = we(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
|
+
V("debugprogressive");
|
|
90
|
+
function V(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 Me() {
|
|
108
|
+
if (Z !== void 0) return Z;
|
|
109
|
+
const i = globalThis.navigator?.userAgent || "";
|
|
110
|
+
return Z = /iPhone|iPad|iPod|Android|IEMobile/i.test(i), V("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", Z), Z;
|
|
97
111
|
}
|
|
98
|
-
let
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
let Z;
|
|
113
|
+
function Fe() {
|
|
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,85 +145,86 @@ 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 _t = typeof window > "u" && typeof document > "u", ve = /* @__PURE__ */ Symbol("needle:raycast-mesh");
|
|
166
|
+
function ne(i) {
|
|
167
|
+
return i?.[ve] instanceof j ? i[ve] : null;
|
|
153
168
|
}
|
|
154
|
-
function
|
|
155
|
-
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !
|
|
156
|
-
const s =
|
|
157
|
-
s.userData = { isRaycastMesh: !0 }, i[
|
|
169
|
+
function Mt(i, t) {
|
|
170
|
+
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !ne(i)) {
|
|
171
|
+
const s = Tt(t);
|
|
172
|
+
s.userData = { isRaycastMesh: !0 }, i[ve] = 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 (ee) return;
|
|
178
|
+
const t = ee = K.prototype.raycast;
|
|
179
|
+
K.prototype.raycast = function(e, s) {
|
|
180
|
+
const r = this, n = ne(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 (!ee) return;
|
|
186
|
+
K.prototype.raycast = ee, ee = null;
|
|
172
187
|
}
|
|
173
188
|
}
|
|
174
|
-
let
|
|
175
|
-
function
|
|
176
|
-
const t = new
|
|
189
|
+
let ee = null;
|
|
190
|
+
function Tt(i) {
|
|
191
|
+
const t = new j();
|
|
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 F = new Array(), h = V("debugprogressive");
|
|
197
|
+
let re, X = -1;
|
|
198
|
+
if (h && typeof window < "u") {
|
|
184
199
|
let i = function() {
|
|
185
|
-
|
|
186
|
-
}
|
|
200
|
+
X += 1, X >= t && (X = -1), console.log(`Toggle LOD level [${X}]`);
|
|
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" && (re = !re, console.log(`Toggle wireframe [${re}]`));
|
|
189
205
|
const s = parseInt(e.key);
|
|
190
|
-
!isNaN(s) && s >= 0 && (
|
|
206
|
+
!isNaN(s) && s >= 0 && (X = s, console.log(`Set LOD level to [${X}]`));
|
|
191
207
|
});
|
|
192
208
|
}
|
|
193
|
-
function
|
|
194
|
-
if (
|
|
209
|
+
function Ue(i) {
|
|
210
|
+
if (h && re !== 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
|
+
Ue(t);
|
|
214
|
+
else i && "wireframe" in i && (i.wireframe = re === !0);
|
|
199
215
|
}
|
|
200
|
-
const
|
|
201
|
-
let
|
|
202
|
-
const
|
|
203
|
-
function
|
|
204
|
-
if (
|
|
205
|
-
const s =
|
|
206
|
-
|
|
216
|
+
const te = new Array();
|
|
217
|
+
let Ot = 0;
|
|
218
|
+
const St = Me() ? 2 : 10;
|
|
219
|
+
function Rt(i) {
|
|
220
|
+
if (te.length < St) {
|
|
221
|
+
const s = te.length;
|
|
222
|
+
h && console.warn(`[Worker] Creating new worker #${s}`);
|
|
207
223
|
const r = be.createWorker(i || {});
|
|
208
|
-
return
|
|
224
|
+
return te.push(r), r;
|
|
209
225
|
}
|
|
210
|
-
const t =
|
|
211
|
-
return
|
|
226
|
+
const t = Ot++ % te.length;
|
|
227
|
+
return te[t];
|
|
212
228
|
}
|
|
213
229
|
class be {
|
|
214
230
|
constructor(t, e) {
|
|
@@ -216,11 +232,11 @@ class be {
|
|
|
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) => {
|
|
@@ -242,22 +258,29 @@ class be {
|
|
|
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
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
const a = _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
|
+
}
|
|
276
|
+
const l = {
|
|
254
277
|
type: "load",
|
|
255
278
|
url: t,
|
|
256
279
|
dracoDecoderPath: s.dracoDecoderPath,
|
|
257
280
|
ktx2TranscoderPath: s.ktx2TranscoderPath,
|
|
258
|
-
ktx2LoaderConfig:
|
|
281
|
+
ktx2LoaderConfig: a
|
|
259
282
|
};
|
|
260
|
-
return this._debug && console.debug("[Worker] Sending load request",
|
|
283
|
+
return this._debug && console.debug("[Worker] Sending load request", l), this.worker.postMessage(l), new Promise((u) => {
|
|
261
284
|
this._running.push({
|
|
262
285
|
url: t.toString(),
|
|
263
286
|
resolve: u
|
|
@@ -266,32 +289,32 @@ 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 j();
|
|
272
295
|
if (s.name = e.name || "", e.index) {
|
|
273
296
|
const r = e.index;
|
|
274
|
-
s.setIndex(
|
|
297
|
+
s.setIndex(fe(r));
|
|
275
298
|
}
|
|
276
299
|
for (const r in e.attributes) {
|
|
277
|
-
const
|
|
278
|
-
s.setAttribute(r,
|
|
300
|
+
const n = e.attributes[r], o = fe(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((a) => fe(a));
|
|
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 ue(), s.boundingBox.min = new k(
|
|
286
309
|
e.boundingBox?.min.x,
|
|
287
310
|
e.boundingBox?.min.y,
|
|
288
311
|
e.boundingBox?.min.z
|
|
289
|
-
), s.boundingBox.max = new
|
|
312
|
+
), s.boundingBox.max = new k(
|
|
290
313
|
e.boundingBox?.max.x,
|
|
291
314
|
e.boundingBox?.max.y,
|
|
292
315
|
e.boundingBox?.max.z
|
|
293
|
-
), s.boundingSphere = new
|
|
294
|
-
new
|
|
316
|
+
), s.boundingSphere = new Ae(
|
|
317
|
+
new k(
|
|
295
318
|
e.boundingSphere?.center.x,
|
|
296
319
|
e.boundingSphere?.center.y,
|
|
297
320
|
e.boundingSphere?.center.z
|
|
@@ -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 } = Le(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 q(
|
|
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,66 +365,89 @@ function Mt(i) {
|
|
|
342
365
|
}
|
|
343
366
|
return i;
|
|
344
367
|
}
|
|
345
|
-
function
|
|
368
|
+
function fe(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 = V("gltf-progressive-worker");
|
|
377
|
+
V("gltf-progressive-reduce-mipmaps");
|
|
378
|
+
const se = V("gltf-progressive-gc"), de = /* @__PURE__ */ Symbol("needle-progressive-texture"), U = "NEEDLE_progressive";
|
|
379
|
+
class g {
|
|
357
380
|
/** The name of the extension */
|
|
358
381
|
get name() {
|
|
359
|
-
return
|
|
382
|
+
return U;
|
|
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,
|
|
376
422
|
lods: []
|
|
377
423
|
}), Array.isArray(t)) {
|
|
378
|
-
for (const
|
|
379
|
-
this.getMaterialMinMaxLODsCount(
|
|
424
|
+
for (const a of t)
|
|
425
|
+
this.getMaterialMinMaxLODsCount(a, e);
|
|
380
426
|
return t[r] = e, e;
|
|
381
427
|
}
|
|
382
|
-
if (
|
|
383
|
-
const
|
|
384
|
-
for (const
|
|
385
|
-
const u =
|
|
386
|
-
u?.isTexture === !0 &&
|
|
428
|
+
if (h === "verbose" && console.log("getMaterialMinMaxLODsCount", t), t.type === "ShaderMaterial" || t.type === "RawShaderMaterial") {
|
|
429
|
+
const a = t;
|
|
430
|
+
for (const l of Object.keys(a.uniforms)) {
|
|
431
|
+
const u = a.uniforms[l].value;
|
|
432
|
+
u?.isTexture === !0 && o(u, e);
|
|
387
433
|
}
|
|
388
434
|
} else if (t.isMaterial)
|
|
389
|
-
for (const
|
|
390
|
-
const
|
|
391
|
-
|
|
435
|
+
for (const a of Object.keys(t)) {
|
|
436
|
+
const l = t[a];
|
|
437
|
+
l?.isTexture === !0 && o(l, e);
|
|
392
438
|
}
|
|
393
439
|
else
|
|
394
|
-
|
|
440
|
+
h && console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);
|
|
395
441
|
return t[r] = e, e;
|
|
396
|
-
function
|
|
397
|
-
const u = s.getAssignedLODInformation(
|
|
442
|
+
function o(a, l) {
|
|
443
|
+
const u = s.getAssignedLODInformation(a);
|
|
398
444
|
if (u) {
|
|
399
|
-
const
|
|
400
|
-
if (
|
|
401
|
-
|
|
402
|
-
for (let
|
|
403
|
-
const
|
|
404
|
-
|
|
445
|
+
const f = s.lodInfos.get(u.key);
|
|
446
|
+
if (f && f.lods) {
|
|
447
|
+
l.min_count = Math.min(l.min_count, f.lods.length), l.max_count = Math.max(l.max_count, f.lods.length);
|
|
448
|
+
for (let c = 0; c < f.lods.length; c++) {
|
|
449
|
+
const d = f.lods[c];
|
|
450
|
+
d.width && (l.lods[c] = l.lods[c] || { min_height: 1 / 0, max_height: 0 }, l.lods[c].min_height = Math.min(l.lods[c].min_height, d.height), l.lods[c].max_height = Math.max(l.lods[c].max_height, d.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 K || 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 a of F)
|
|
509
|
+
a.onBeforeGetLODMesh?.(t, e);
|
|
510
|
+
t["LOD:requested level"] = e;
|
|
511
|
+
const o = () => t["LOD:requested level"] === e || this.shouldApplyStaleMeshLOD(t, e);
|
|
512
|
+
return g.getOrLoadLOD(r, e, {
|
|
513
|
+
isCurrent: o
|
|
514
|
+
}).then((a) => {
|
|
515
|
+
if (Array.isArray(a)) {
|
|
516
|
+
const u = n.index || 0;
|
|
517
|
+
a = a[u];
|
|
468
518
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
519
|
+
const l = t["LOD:requested level"] === e;
|
|
520
|
+
return (l || this.shouldApplyStaleMeshLOD(t, e)) && (l && delete t["LOD:requested level"], a && r != a && (a?.isBufferGeometry ? typeof s?.apply == "function" ? s.apply(a, e, t) : s?.apply !== !1 && (t.geometry = a) : h && console.error("Invalid LOD geometry", a))), a;
|
|
521
|
+
}).catch((a) => (console.error("Error loading mesh LOD", t, a), null));
|
|
522
|
+
} else h && 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 a of n.material) {
|
|
533
|
+
const l = this.assignTextureLOD(a, e, s);
|
|
534
|
+
o.push(l);
|
|
483
535
|
}
|
|
484
|
-
return Promise.all(
|
|
485
|
-
const
|
|
486
|
-
for (const
|
|
487
|
-
Array.isArray(
|
|
488
|
-
return
|
|
536
|
+
return Promise.all(o).then((a) => {
|
|
537
|
+
const l = new Array();
|
|
538
|
+
for (const u of a)
|
|
539
|
+
Array.isArray(u) && l.push(...u);
|
|
540
|
+
return l;
|
|
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 = [], a = new Array();
|
|
547
|
+
if (this.trackCurrentMaterialTextureSlots(n), n.uniforms && (n.isRawShaderMaterial || n.isShaderMaterial === !0)) {
|
|
548
|
+
const l = n;
|
|
549
|
+
for (const u of Object.keys(l.uniforms)) {
|
|
550
|
+
const f = l.uniforms[u].value;
|
|
551
|
+
if (f?.isTexture === !0) {
|
|
552
|
+
const c = this.assignTextureLODForSlot(f, e, n, u, r).then((d) => (d && l.uniforms[u].value != d && (l.uniforms[u].value = d, l.uniformsNeedUpdate = !0), d));
|
|
553
|
+
o.push(c), a.push(u);
|
|
502
554
|
}
|
|
503
555
|
}
|
|
504
556
|
} else
|
|
505
|
-
for (const
|
|
506
|
-
const
|
|
507
|
-
if (
|
|
508
|
-
const
|
|
509
|
-
|
|
557
|
+
for (const l of Object.keys(n)) {
|
|
558
|
+
const u = n[l];
|
|
559
|
+
if (u?.isTexture === !0) {
|
|
560
|
+
const f = this.assignTextureLODForSlot(u, e, n, l, r);
|
|
561
|
+
o.push(f), a.push(l);
|
|
510
562
|
}
|
|
511
563
|
}
|
|
512
|
-
return Promise.all(
|
|
513
|
-
const
|
|
514
|
-
for (let
|
|
515
|
-
const
|
|
516
|
-
|
|
564
|
+
return Promise.all(o).then((l) => {
|
|
565
|
+
const u = new Array();
|
|
566
|
+
for (let f = 0; f < l.length; f++) {
|
|
567
|
+
const c = l[f], d = a[f];
|
|
568
|
+
c && c.isTexture === !0 ? u.push({ material: n, slot: d, texture: c, level: e }) : u.push({ material: n, slot: d, 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 q || 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
|
+
g.queue.maxConcurrent = t;
|
|
533
585
|
}
|
|
534
586
|
static get maxConcurrentLoadingTasks() {
|
|
535
|
-
return
|
|
587
|
+
return g.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 a = s && r ? this.nextTextureSlotRequestId(s, r, e, n) : 0, l = () => !s || !r || this.getLatestTextureSlotRequest(s, r)?.id === a, u = () => l() || this.shouldApplyStaleTextureSlotLOD(s, r, e, n), f = g.getOrLoadLOD(t, e, {
|
|
604
|
+
isCurrent: u
|
|
605
|
+
}).then((c) => {
|
|
606
|
+
if (!l() && !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
|
|
609
|
+
if (c?.isTexture === !0) {
|
|
610
|
+
if (c != t && s && r) {
|
|
611
|
+
const d = this.getMaterialTextureSlot(s, r) ?? t;
|
|
612
|
+
if (d && !n) {
|
|
613
|
+
const p = this.getAssignedLODInformation(d);
|
|
614
|
+
if (p && p?.level < e)
|
|
615
|
+
return h === "verbose" && console.warn("Assigned texture level is already higher: ", p.level, e, s, d, c), null;
|
|
549
616
|
}
|
|
550
|
-
|
|
551
|
-
const a = this.getAssignedLODInformation(n);
|
|
552
|
-
console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level ?? "?"} → ${e} for ${s.name || s.type}.${r}`, n.uuid);
|
|
553
|
-
}
|
|
554
|
-
s[r] = o;
|
|
617
|
+
this.assignTrackedTextureSlot(s, r, c);
|
|
555
618
|
}
|
|
556
|
-
return
|
|
557
|
-
} else
|
|
619
|
+
return c;
|
|
620
|
+
} else h == "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, a, f), f;
|
|
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), a = this.getAssignedLODInformation(o)?.level ?? 1 / 0;
|
|
660
|
+
return s >= a ? !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 a = this.pendingTextureSlotRequests.get(t);
|
|
670
|
+
a || (a = /* @__PURE__ */ new Map(), this.pendingTextureSlotRequests.set(t, a));
|
|
671
|
+
const l = { level: s, force: r, id: n, promise: o };
|
|
672
|
+
a.set(e, l), o.finally(() => {
|
|
673
|
+
a.get(e)?.id === n && a.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) && (h || se)) {
|
|
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
|
+
h && 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?.[U];
|
|
721
|
+
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && g.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 h && 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[U];
|
|
591
741
|
if (r) {
|
|
592
742
|
if (!r.lods) {
|
|
593
|
-
|
|
743
|
+
h && 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, g.registerTexture(this.url, o, r.lods?.length, s, r));
|
|
749
|
+
n || this.parser.getDependency("texture", s).then((o) => {
|
|
750
|
+
o && g.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[U];
|
|
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 && g.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
|
+
h && console.error("!! gltf-progressive: Called register texture without texture");
|
|
623
781
|
return;
|
|
624
782
|
}
|
|
625
|
-
if (
|
|
626
|
-
const { width:
|
|
627
|
-
console.log(`> gltf-progressive: register texture[${r}] "${e.name || e.uuid}", Current: ${
|
|
783
|
+
if (h) {
|
|
784
|
+
const { width: a, height: l } = Le(e);
|
|
785
|
+
console.log(`> gltf-progressive: register texture[${r}] "${e.name || e.uuid}", Current: ${a}x${l}, 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[de] = n);
|
|
788
|
+
const o = n.guid;
|
|
789
|
+
g.assignLODInformation(t, e, o, s, r), g.lodInfos.set(o, n), g.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,
|
|
637
|
-
const
|
|
638
|
-
if (!
|
|
639
|
-
|
|
804
|
+
static registerMesh = (t, e, s, r, n, o) => {
|
|
805
|
+
const a = s.geometry;
|
|
806
|
+
if (!a) {
|
|
807
|
+
h && console.warn("gltf-progressive: Register mesh without geometry");
|
|
640
808
|
return;
|
|
641
809
|
}
|
|
642
|
-
|
|
643
|
-
let u =
|
|
644
|
-
u ? u.push(s.geometry) : u = [s.geometry],
|
|
645
|
-
for (const
|
|
646
|
-
|
|
810
|
+
a.userData || (a.userData = {}), h && console.log("> Progressive: register mesh " + s.name, { index: n, uuid: s.uuid }, o, s), g.assignLODInformation(t, a, e, r, n), g.lodInfos.set(e, o);
|
|
811
|
+
let u = g.lowresCache.get(e)?.deref();
|
|
812
|
+
u ? u.push(s.geometry) : u = [s.geometry], g.lowresCache.set(e, new WeakRef(u)), r > 0 && !ne(s) && Mt(s, a);
|
|
813
|
+
for (const f of F)
|
|
814
|
+
f.onRegisteredNewMesh?.(s, o);
|
|
647
815
|
};
|
|
648
816
|
/**
|
|
649
817
|
* Dispose cached resources to free memory.
|
|
@@ -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. */
|
|
@@ -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 = g.cache.get(t);
|
|
888
|
+
(h || se) && console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
889
|
+
${t}`), e instanceof WeakRef && (e.deref() || (g.cache.delete(t), (h || se) && 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), h === "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,196 @@ ${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 (h === "verbose" || se) && 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:
|
|
745
|
-
const
|
|
746
|
-
let
|
|
747
|
-
|
|
909
|
+
return this.textureRefCounts.delete(e), (h || se) && n("[gltf-progressive] Memory: Dispose texture", r), t.dispose(), !0;
|
|
910
|
+
return this.textureRefCounts.set(e, r), h === "verbose" && n("[gltf-progressive] Memory: Untrack texture", r), !1;
|
|
911
|
+
function n(o, a) {
|
|
912
|
+
let { width: l, height: u } = Le(t);
|
|
913
|
+
const f = l && u ? `${l}x${u}` : "N/A";
|
|
914
|
+
let c = "N/A";
|
|
915
|
+
l && u && (c = `~${(vt(t) / (1024 * 1024)).toFixed(2)} MB`), console.log(`${o} — ${t.name} ${f} (${c}), refCount: ${s} → ${a}
|
|
748
916
|
${e}`);
|
|
749
917
|
}
|
|
750
918
|
}
|
|
751
919
|
static workers = [];
|
|
752
920
|
static _workersIndex = 0;
|
|
753
|
-
static async getOrLoadLOD(t, e) {
|
|
754
|
-
const
|
|
755
|
-
if (!
|
|
756
|
-
return
|
|
757
|
-
const o =
|
|
758
|
-
let
|
|
921
|
+
static async getOrLoadLOD(t, e, s) {
|
|
922
|
+
const r = h == "verbose", n = this.getAssignedLODInformation(t);
|
|
923
|
+
if (!n)
|
|
924
|
+
return h && console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`, t), null;
|
|
925
|
+
const o = n?.key;
|
|
926
|
+
let a;
|
|
759
927
|
if (t.isTexture === !0) {
|
|
760
|
-
const
|
|
761
|
-
|
|
928
|
+
const u = t;
|
|
929
|
+
u.source && u.source[de] && (a = u.source[de]);
|
|
762
930
|
}
|
|
763
|
-
if (
|
|
764
|
-
|
|
931
|
+
if (a || (a = g.lodInfos.get(o)), !a)
|
|
932
|
+
h && console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`, t.type, g.lodInfos);
|
|
765
933
|
else {
|
|
766
934
|
if (e > 0) {
|
|
767
935
|
let c = !1;
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
770
|
-
const
|
|
771
|
-
if (
|
|
772
|
-
const
|
|
773
|
-
if (
|
|
774
|
-
this.lowresCache.delete(o),
|
|
936
|
+
const d = Array.isArray(a.lods);
|
|
937
|
+
if (d && e >= a.lods.length ? c = !0 : d || (c = !0), c) {
|
|
938
|
+
const p = this.lowresCache.get(o);
|
|
939
|
+
if (p) {
|
|
940
|
+
const b = p.deref();
|
|
941
|
+
if (b) return b;
|
|
942
|
+
this.lowresCache.delete(o), h && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`);
|
|
775
943
|
}
|
|
776
944
|
return null;
|
|
777
945
|
}
|
|
778
946
|
}
|
|
779
|
-
const
|
|
780
|
-
if (!
|
|
781
|
-
return
|
|
782
|
-
const
|
|
783
|
-
if (
|
|
784
|
-
if (!
|
|
785
|
-
return console.warn("missing pointer for glb/gltf texture",
|
|
786
|
-
const c =
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const _ = n, R = new Promise(async (d, g) => {
|
|
805
|
-
if (Dt) {
|
|
806
|
-
const x = await (await bt({})).load(u);
|
|
807
|
-
if (x.textures.length > 0)
|
|
808
|
-
for (const h of x.textures) {
|
|
809
|
-
let p = h.texture;
|
|
810
|
-
return m.assignLODInformation(r.url, p, o, e, void 0), t instanceof F && (p = this.copySettings(t, p)), p && (p.guid = _.guid), d(p);
|
|
947
|
+
const u = Array.isArray(a.lods) ? a.lods[e]?.path : a.lods;
|
|
948
|
+
if (!u)
|
|
949
|
+
return h && !a["missing:uri"] && (a["missing:uri"] = !0, console.warn("Missing uri for progressive asset for LOD " + e, a)), null;
|
|
950
|
+
const f = wt(n.url, u);
|
|
951
|
+
if (f.endsWith(".glb") || f.endsWith(".gltf")) {
|
|
952
|
+
if (!a.guid)
|
|
953
|
+
return console.warn("missing pointer for glb/gltf texture", a), null;
|
|
954
|
+
const c = f + "_" + a.guid, d = await this.tryResolveLODCacheEntry(this.cache.get(c), c, f, t, e, r);
|
|
955
|
+
if (d.found) return d.value;
|
|
956
|
+
if (s?.isCurrent?.() === !1)
|
|
957
|
+
return r && console.log(`Skipping stale LOD ${e} request before queue: ${f}`), null;
|
|
958
|
+
const p = await this.queue.slot(f);
|
|
959
|
+
if (s?.isCurrent?.() === !1)
|
|
960
|
+
return r && console.log(`Skipping stale LOD ${e} request after queue: ${f}`), null;
|
|
961
|
+
const b = await this.tryResolveLODCacheEntry(this.cache.get(c), c, f, t, e, r);
|
|
962
|
+
if (b.found) return b.value;
|
|
963
|
+
if (!p.use)
|
|
964
|
+
return h && console.log(`LOD ${e} was aborted: ${f}`), null;
|
|
965
|
+
const y = a, x = new Promise(async (w, z) => {
|
|
966
|
+
if (Ct) {
|
|
967
|
+
const m = await (await Rt({})).load(f);
|
|
968
|
+
if (m.textures.length > 0)
|
|
969
|
+
for (const D of m.textures) {
|
|
970
|
+
let M = D.texture;
|
|
971
|
+
return g.assignLODInformation(n.url, M, o, e, void 0), t instanceof q && (M = this.copySettings(t, M)), M && (M.guid = y.guid), w(M);
|
|
811
972
|
}
|
|
812
|
-
if (
|
|
813
|
-
const
|
|
814
|
-
for (const
|
|
815
|
-
const
|
|
816
|
-
|
|
973
|
+
if (m.geometries.length > 0) {
|
|
974
|
+
const D = new Array();
|
|
975
|
+
for (const M of m.geometries) {
|
|
976
|
+
const G = M.geometry;
|
|
977
|
+
g.assignLODInformation(n.url, G, o, e, M.primitiveIndex), D.push(G);
|
|
817
978
|
}
|
|
818
|
-
return
|
|
979
|
+
return w(D);
|
|
819
980
|
}
|
|
820
|
-
return
|
|
981
|
+
return w(null);
|
|
821
982
|
}
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
let
|
|
825
|
-
if (
|
|
826
|
-
const
|
|
827
|
-
|
|
983
|
+
const v = new De();
|
|
984
|
+
Ge(v), h && (await new Promise((_) => setTimeout(_, 1e3)), r && console.warn("Start loading (delayed) " + f, y.guid));
|
|
985
|
+
let T = f;
|
|
986
|
+
if (y && Array.isArray(y.lods)) {
|
|
987
|
+
const _ = y.lods[e];
|
|
988
|
+
_.hash && (T += "?v=" + _.hash);
|
|
828
989
|
}
|
|
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
|
-
|
|
990
|
+
const O = await v.loadAsync(T).catch((_) => (console.error(`Error loading LOD ${e} from ${f}
|
|
991
|
+
`, _), w(null)));
|
|
992
|
+
if (!O)
|
|
993
|
+
return w(null);
|
|
994
|
+
const P = O.parser;
|
|
995
|
+
r && console.log("Loading finished " + f, y.guid);
|
|
996
|
+
let S = 0;
|
|
997
|
+
if (O.parser.json.textures) {
|
|
998
|
+
let _ = !1;
|
|
999
|
+
for (const m of O.parser.json.textures) {
|
|
1000
|
+
if (m?.extensions) {
|
|
1001
|
+
const D = m?.extensions[U];
|
|
1002
|
+
if (D?.guid && D.guid === y.guid) {
|
|
1003
|
+
_ = !0;
|
|
843
1004
|
break;
|
|
844
1005
|
}
|
|
845
1006
|
}
|
|
846
|
-
|
|
1007
|
+
S++;
|
|
847
1008
|
}
|
|
848
|
-
if (
|
|
849
|
-
let
|
|
850
|
-
return
|
|
851
|
-
} else
|
|
1009
|
+
if (_) {
|
|
1010
|
+
let m = await P.getDependency("texture", S);
|
|
1011
|
+
return m && g.assignLODInformation(n.url, m, o, e, void 0), r && console.log('change "' + t.name + '" → "' + m.name + '"', f, S, m, c), t instanceof q && (m = this.copySettings(t, m)), m && (m.guid = y.guid), w(m);
|
|
1012
|
+
} else h && console.warn("Could not find texture with guid", y.guid, O.parser.json);
|
|
852
1013
|
}
|
|
853
|
-
if (
|
|
854
|
-
let
|
|
855
|
-
for (const
|
|
856
|
-
if (
|
|
857
|
-
const
|
|
858
|
-
if (
|
|
859
|
-
|
|
1014
|
+
if (S = 0, O.parser.json.meshes) {
|
|
1015
|
+
let _ = !1;
|
|
1016
|
+
for (const m of O.parser.json.meshes) {
|
|
1017
|
+
if (m?.extensions) {
|
|
1018
|
+
const D = m?.extensions[U];
|
|
1019
|
+
if (D?.guid && D.guid === y.guid) {
|
|
1020
|
+
_ = !0;
|
|
860
1021
|
break;
|
|
861
1022
|
}
|
|
862
1023
|
}
|
|
863
|
-
|
|
1024
|
+
S++;
|
|
864
1025
|
}
|
|
865
|
-
if (
|
|
866
|
-
const
|
|
867
|
-
if (
|
|
868
|
-
const
|
|
869
|
-
return
|
|
1026
|
+
if (_) {
|
|
1027
|
+
const m = await P.getDependency("mesh", S);
|
|
1028
|
+
if (r && console.log(`Loaded Mesh "${m.name}"`, f, S, m, c), m.isMesh === !0) {
|
|
1029
|
+
const D = m.geometry;
|
|
1030
|
+
return g.assignLODInformation(n.url, D, o, e, 0), w(D);
|
|
870
1031
|
} else {
|
|
871
|
-
const
|
|
872
|
-
for (let
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
875
|
-
const
|
|
876
|
-
|
|
1032
|
+
const D = new Array();
|
|
1033
|
+
for (let M = 0; M < m.children.length; M++) {
|
|
1034
|
+
const G = m.children[M];
|
|
1035
|
+
if (G.isMesh === !0) {
|
|
1036
|
+
const ie = G.geometry;
|
|
1037
|
+
g.assignLODInformation(n.url, ie, o, e, M), D.push(ie);
|
|
877
1038
|
}
|
|
878
1039
|
}
|
|
879
|
-
return
|
|
1040
|
+
return w(D);
|
|
880
1041
|
}
|
|
881
|
-
} else
|
|
1042
|
+
} else h && console.warn("Could not find mesh with guid", y.guid, O.parser.json);
|
|
882
1043
|
}
|
|
883
|
-
return
|
|
1044
|
+
return w(null);
|
|
884
1045
|
});
|
|
885
|
-
this.cache.set(c,
|
|
886
|
-
const
|
|
887
|
-
return
|
|
888
|
-
} else if (t instanceof
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1046
|
+
this.cache.set(c, x), p.use(x);
|
|
1047
|
+
const L = await x;
|
|
1048
|
+
return L != null ? L instanceof q ? (this.cache.set(c, new WeakRef(L)), g._resourceRegistry.register(L, c)) : Array.isArray(L) ? this.cache.set(c, Promise.resolve(L)) : this.cache.set(c, Promise.resolve(L)) : this.cache.set(c, Promise.resolve(null)), L;
|
|
1049
|
+
} else if (t instanceof q) {
|
|
1050
|
+
if (s?.isCurrent?.() === !1)
|
|
1051
|
+
return r && console.log(`Skipping stale texture LOD ${e} request: ${f}`), null;
|
|
1052
|
+
r && console.log("Load texture from uri: " + f);
|
|
1053
|
+
const d = await new it().loadAsync(f);
|
|
1054
|
+
return s?.isCurrent?.() === !1 ? (d?.dispose(), null) : (d ? (d.guid = a.guid, d.flipY = !1, d.needsUpdate = !0, d.colorSpace = t.colorSpace, r && console.log(a, d)) : h && console.warn("failed loading", f), d);
|
|
892
1055
|
}
|
|
893
1056
|
}
|
|
894
1057
|
return null;
|
|
895
1058
|
}
|
|
896
|
-
static
|
|
897
|
-
|
|
1059
|
+
static async tryResolveLODCacheEntry(t, e, s, r, n, o) {
|
|
1060
|
+
if (t === void 0)
|
|
1061
|
+
return { found: !1 };
|
|
1062
|
+
if (o && console.log(`LOD ${n} was already loading/loaded: ${e}`), t instanceof WeakRef) {
|
|
1063
|
+
const u = t.deref();
|
|
1064
|
+
if (u) {
|
|
1065
|
+
let f = u, c = !1;
|
|
1066
|
+
if (f instanceof q && r instanceof q ? Te(f.image) || we(f) ? f = this.copySettings(r, f) : c = !0 : f instanceof j && r instanceof j && (f.attributes.position?.array || (c = !0)), !c)
|
|
1067
|
+
return { found: !0, value: f };
|
|
1068
|
+
}
|
|
1069
|
+
return this.cache.delete(e), h && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`), { found: !1 };
|
|
1070
|
+
}
|
|
1071
|
+
let a = await t.catch((u) => (console.error(`Error loading LOD ${n} from ${s}
|
|
1072
|
+
`, u), null)), l = !1;
|
|
1073
|
+
return a == null || (a instanceof q && r instanceof q ? Te(a.image) || we(a) ? a = this.copySettings(r, a) : (l = !0, this.cache.delete(e)) : a instanceof j && r instanceof j && (a.attributes.position?.array || (l = !0, this.cache.delete(e)))), l ? { found: !1 } : { found: !0, value: a };
|
|
1074
|
+
}
|
|
1075
|
+
static _queue;
|
|
1076
|
+
static get queue() {
|
|
1077
|
+
return this._queue ??= new Lt(Me() ? 20 : 50, { debug: h != !1 });
|
|
1078
|
+
}
|
|
1079
|
+
static assignLODInformation(t, e, s, r, n) {
|
|
898
1080
|
if (!e) return;
|
|
899
1081
|
e.userData || (e.userData = {});
|
|
900
|
-
const
|
|
901
|
-
e.userData.LODS =
|
|
1082
|
+
const o = new kt(t, s, r, n);
|
|
1083
|
+
e.userData.LODS = o, "source" in e && typeof e.source == "object" && (e.source.LODS = o);
|
|
902
1084
|
}
|
|
903
1085
|
static getAssignedLODInformation(t) {
|
|
904
1086
|
return t ? t.userData?.LODS ? t.userData.LODS : "source" in t && t.source?.LODS ? t.source.LODS : null : null;
|
|
905
1087
|
}
|
|
906
1088
|
// private static readonly _copiedTextures: WeakMap<Texture, Texture> = new Map();
|
|
907
1089
|
static copySettings(t, e) {
|
|
908
|
-
return e ? (
|
|
1090
|
+
return e ? (h === "verbose" && console.debug(`Copy texture settings
|
|
909
1091
|
`, t.uuid, `
|
|
910
1092
|
`, 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
1093
|
}
|
|
912
1094
|
}
|
|
913
|
-
class
|
|
1095
|
+
class kt {
|
|
914
1096
|
url;
|
|
915
1097
|
/** the key to lookup the LOD information */
|
|
916
1098
|
key;
|
|
@@ -921,10 +1103,10 @@ class Ot {
|
|
|
921
1103
|
this.url = t, this.key = e, this.level = s, r != null && (this.index = r);
|
|
922
1104
|
}
|
|
923
1105
|
}
|
|
924
|
-
class
|
|
1106
|
+
class oe {
|
|
925
1107
|
static addPromise = (t, e, s, r) => {
|
|
926
|
-
r.forEach((
|
|
927
|
-
|
|
1108
|
+
r.forEach((n) => {
|
|
1109
|
+
n.add(t, e, s);
|
|
928
1110
|
});
|
|
929
1111
|
};
|
|
930
1112
|
ready;
|
|
@@ -955,8 +1137,8 @@ class ce {
|
|
|
955
1137
|
_maxPromisesPerObject = 1;
|
|
956
1138
|
constructor(t, e) {
|
|
957
1139
|
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 =
|
|
1140
|
+
this._frame_start = e.waitForFirstCapture ? void 0 : t, this._frames_to_capture = r, this.ready = new Promise((n) => {
|
|
1141
|
+
this._resolve = n;
|
|
960
1142
|
}), this.ready.finally(() => {
|
|
961
1143
|
this._resolved = !0, this._awaiting.length = 0;
|
|
962
1144
|
}), this._signal = e.signal, this._signal?.addEventListener("abort", () => {
|
|
@@ -970,15 +1152,15 @@ class ce {
|
|
|
970
1152
|
_seen = /* @__PURE__ */ new WeakMap();
|
|
971
1153
|
add(t, e, s) {
|
|
972
1154
|
if (this._resolved) {
|
|
973
|
-
|
|
1155
|
+
h && console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
|
|
974
1156
|
return;
|
|
975
1157
|
}
|
|
976
1158
|
if (!(this._frame_start !== void 0 && this._currentFrame > this._frame_start + this._frames_to_capture)) {
|
|
977
1159
|
if (this._maxPromisesPerObject >= 1)
|
|
978
1160
|
if (this._seen.has(e)) {
|
|
979
|
-
|
|
1161
|
+
const r = this._seen.get(e);
|
|
980
1162
|
if (r >= this._maxPromisesPerObject) {
|
|
981
|
-
|
|
1163
|
+
h && console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");
|
|
982
1164
|
return;
|
|
983
1165
|
}
|
|
984
1166
|
this._seen.set(e, r + 1);
|
|
@@ -997,22 +1179,122 @@ class ce {
|
|
|
997
1179
|
});
|
|
998
1180
|
}
|
|
999
1181
|
}
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1182
|
+
const A = V("debugprogressive"), At = A === "colors", It = V("noprogressive"), he = /* @__PURE__ */ Symbol("Needle:LODSManager"), ge = /* @__PURE__ */ Symbol("Needle:LODState"), E = /* @__PURE__ */ Symbol("Needle:CurrentLOD"), Oe = Xe, R = { mesh_lod: -1, texture_lod: -1 }, $t = new Ie(), Se = [
|
|
1183
|
+
3526751,
|
|
1184
|
+
11065402,
|
|
1185
|
+
15978811,
|
|
1186
|
+
15897394,
|
|
1187
|
+
15749691,
|
|
1188
|
+
11032304,
|
|
1189
|
+
4827122,
|
|
1190
|
+
3332036,
|
|
1191
|
+
16739229,
|
|
1192
|
+
7306743,
|
|
1193
|
+
14053330,
|
|
1194
|
+
3516499,
|
|
1195
|
+
12035359,
|
|
1196
|
+
14703919,
|
|
1197
|
+
3963096,
|
|
1198
|
+
42662,
|
|
1199
|
+
14100029,
|
|
1200
|
+
8344319,
|
|
1201
|
+
4633680,
|
|
1202
|
+
16229681,
|
|
1203
|
+
3120096,
|
|
1204
|
+
12076434,
|
|
1205
|
+
9083434,
|
|
1206
|
+
2060171,
|
|
1207
|
+
15751837,
|
|
1208
|
+
10182117,
|
|
1209
|
+
48121,
|
|
1210
|
+
62932,
|
|
1211
|
+
16704576,
|
|
1212
|
+
15817653,
|
|
1213
|
+
5083278,
|
|
1214
|
+
5592405
|
|
1215
|
+
];
|
|
1216
|
+
function Bt() {
|
|
1217
|
+
const i = Oe.Timer || Oe.Clock;
|
|
1218
|
+
return new i();
|
|
1219
|
+
}
|
|
1220
|
+
const pe = new ue(), N = new ue(), Re = new ue(), Gt = new k(), qt = new k(), Wt = new $e(), W = new k(), Y = new k(), Q = new k(), J = new k();
|
|
1221
|
+
function Ft(i, t) {
|
|
1222
|
+
const e = i.min, s = i.max, r = (e.x + s.x) * 0.5, n = (e.y + s.y) * 0.5;
|
|
1223
|
+
return W.set(r, n, e.z).applyMatrix4(t).z < 0;
|
|
1224
|
+
}
|
|
1225
|
+
function Ut(i) {
|
|
1226
|
+
const {
|
|
1227
|
+
geometry: t,
|
|
1228
|
+
matrixWorld: e,
|
|
1229
|
+
camera: s,
|
|
1230
|
+
projectionScreenMatrix: r,
|
|
1231
|
+
desiredDensity: n,
|
|
1232
|
+
canvasHeight: o = 0,
|
|
1233
|
+
currentLevel: a = -1,
|
|
1234
|
+
xrEnabled: l = !1,
|
|
1235
|
+
debugDrawLine: u,
|
|
1236
|
+
warnMissingPrimitiveDensities: f = !1
|
|
1237
|
+
} = i, c = g.getMeshLODExtension(t)?.lods, d = g.getPrimitiveIndex(t), p = i.target ?? {
|
|
1238
|
+
level: a,
|
|
1239
|
+
primitiveIndex: d,
|
|
1240
|
+
screenCoverage: 0,
|
|
1241
|
+
screenspaceVolume: new k(),
|
|
1242
|
+
centrality: 1
|
|
1243
|
+
};
|
|
1244
|
+
p.level = a, p.primitiveIndex = d, p.screenCoverage = 0, p.screenspaceVolume.set(0, 0, 0), p.centrality = 1;
|
|
1245
|
+
let b = i.boundingBox ?? t.boundingBox;
|
|
1246
|
+
if (b || (t.computeBoundingBox(), b = t.boundingBox), !b) return p;
|
|
1247
|
+
if (pe.copy(b).applyMatrix4(e), s.isPerspectiveCamera && Ft(pe, r))
|
|
1248
|
+
return p.level = 0, p.screenCoverage = 1 / 0, p.screenspaceVolume.set(1 / 0, 1 / 0, 1 / 0), p;
|
|
1249
|
+
if (N.copy(pe).applyMatrix4(r), l && s.isPerspectiveCamera && s.fov > 70) {
|
|
1250
|
+
const v = N.min, T = N.max;
|
|
1251
|
+
let O = v.x, P = v.y, S = T.x, _ = T.y;
|
|
1252
|
+
const m = 2, D = 1.5, M = (v.x + T.x) * 0.5, G = (v.y + T.y) * 0.5;
|
|
1253
|
+
O = (O - M) * m + M, P = (P - G) * m + G, S = (S - M) * m + M, _ = (_ - G) * m + G;
|
|
1254
|
+
const ie = O < 0 && S > 0 ? 0 : Math.min(Math.abs(v.x), Math.abs(T.x)), Ve = P < 0 && _ > 0 ? 0 : Math.min(Math.abs(v.y), Math.abs(T.y)), ce = Math.max(ie, Ve);
|
|
1255
|
+
p.centrality = (D - ce) * (D - ce) * (D - ce);
|
|
1256
|
+
}
|
|
1257
|
+
const y = N.getSize(Gt);
|
|
1258
|
+
y.multiplyScalar(0.5), globalThis.screen?.availHeight > 0 && o > 0 && y.multiplyScalar(o / globalThis.screen.availHeight), s.isPerspectiveCamera && (y.x *= s.aspect), Re.copy(b).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);
|
|
1259
|
+
const x = Re.getSize(qt), L = Math.max(y.x, y.y), w = Math.max(x.x, x.y);
|
|
1260
|
+
L !== 0 && w !== 0 && (y.z = x.z / w * L);
|
|
1261
|
+
const z = Math.max(y.x, y.y, y.z) * p.centrality;
|
|
1262
|
+
if (p.screenCoverage = z, p.screenspaceVolume.copy(y), z <= 0) return p;
|
|
1263
|
+
if (u) {
|
|
1264
|
+
const v = Wt.copy(r);
|
|
1265
|
+
v.invert(), W.copy(N.min), Y.copy(N.max), Y.x = W.x, Q.copy(N.max), Q.y = W.y, J.copy(N.max);
|
|
1266
|
+
const T = (W.z + J.z) * 0.5;
|
|
1267
|
+
W.z = Y.z = Q.z = J.z = T, W.applyMatrix4(v), Y.applyMatrix4(v), Q.applyMatrix4(v), J.applyMatrix4(v), u(W, Y, 255), u(W, Q, 255), u(Y, J, 255), u(Q, J, 255);
|
|
1268
|
+
}
|
|
1269
|
+
if (c?.length)
|
|
1270
|
+
for (let v = 0; v < c.length; v++) {
|
|
1271
|
+
const T = c[v], O = T.densities?.[d] || T.density || 1e-5;
|
|
1272
|
+
if (d > 0 && f && Fe() && !T.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.")), O / z < n) {
|
|
1273
|
+
p.level = v;
|
|
1274
|
+
break;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return p;
|
|
1278
|
+
}
|
|
1279
|
+
class I {
|
|
1002
1280
|
/**
|
|
1003
1281
|
* 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
1282
|
*/
|
|
1005
1283
|
static debugDrawLine;
|
|
1006
1284
|
/** @internal */
|
|
1007
1285
|
static getObjectLODState(t) {
|
|
1008
|
-
return t[
|
|
1286
|
+
return t[ge];
|
|
1009
1287
|
}
|
|
1010
1288
|
static addPlugin(t) {
|
|
1011
|
-
|
|
1289
|
+
F.push(t);
|
|
1012
1290
|
}
|
|
1013
1291
|
static removePlugin(t) {
|
|
1014
|
-
const e =
|
|
1015
|
-
e >= 0 &&
|
|
1292
|
+
const e = F.indexOf(t);
|
|
1293
|
+
e >= 0 && F.splice(e, 1);
|
|
1294
|
+
}
|
|
1295
|
+
/** Read-only snapshot of the currently registered plugins, for inspection. Use {@link addPlugin} / {@link removePlugin} to modify the registry. */
|
|
1296
|
+
static getPlugins() {
|
|
1297
|
+
return F;
|
|
1016
1298
|
}
|
|
1017
1299
|
/**
|
|
1018
1300
|
* Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
|
|
@@ -1020,20 +1302,20 @@ class v {
|
|
|
1020
1302
|
* @returns The LODsManager instance.
|
|
1021
1303
|
*/
|
|
1022
1304
|
static get(t, e) {
|
|
1023
|
-
if (t[
|
|
1024
|
-
return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[
|
|
1025
|
-
const s = new
|
|
1305
|
+
if (t[he])
|
|
1306
|
+
return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[he];
|
|
1307
|
+
const s = new I(t, {
|
|
1026
1308
|
engine: "unknown",
|
|
1027
1309
|
...e
|
|
1028
1310
|
});
|
|
1029
|
-
return t[
|
|
1311
|
+
return t[he] = s, s;
|
|
1030
1312
|
}
|
|
1031
1313
|
renderer;
|
|
1032
1314
|
context;
|
|
1033
|
-
projectionScreenMatrix = new
|
|
1315
|
+
projectionScreenMatrix = new $e();
|
|
1034
1316
|
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
1035
1317
|
get plugins() {
|
|
1036
|
-
return
|
|
1318
|
+
return F;
|
|
1037
1319
|
}
|
|
1038
1320
|
/**
|
|
1039
1321
|
* Force override the LOD level for all objects (meshes + textures) rendered in the scene
|
|
@@ -1071,45 +1353,99 @@ class v {
|
|
|
1071
1353
|
_newPromiseGroups = [];
|
|
1072
1354
|
_promiseGroupIds = 0;
|
|
1073
1355
|
/**
|
|
1074
|
-
*
|
|
1356
|
+
* Returns a promise that resolves once all LOD requests initiated during the next render cycles have finished loading.
|
|
1357
|
+
* This is useful for hiding low-resolution placeholders (e.g. with a loading overlay or CSS blur) until high-quality assets are ready.
|
|
1358
|
+
*
|
|
1359
|
+
* By default, the returned promise captures LOD loading requests for 2 frames and resolves when all of them complete.
|
|
1360
|
+
* Use `waitForFirstCapture` if no LOD requests may happen immediately (e.g. after a scene switch).
|
|
1361
|
+
*
|
|
1362
|
+
* @param opts - Optional configuration for how long to capture and what to wait for. See {@link PromiseGroupOptions}.
|
|
1363
|
+
* @returns A promise that resolves with `{ cancelled, awaited_count, resolved_count }` once all captured LOD loads complete (or the signal aborts).
|
|
1364
|
+
*
|
|
1365
|
+
* @example
|
|
1366
|
+
* ```ts
|
|
1367
|
+
* // Wait for initial LODs to finish loading, then remove a blur overlay
|
|
1368
|
+
* const result = await lodsManager.awaitLoading({
|
|
1369
|
+
* frames: 5,
|
|
1370
|
+
* signal: AbortSignal.timeout(10_000),
|
|
1371
|
+
* });
|
|
1372
|
+
* console.log(`Loaded ${result.resolved_count} of ${result.awaited_count} LODs`);
|
|
1373
|
+
* document.querySelector('.blur-overlay')?.remove();
|
|
1374
|
+
* ```
|
|
1375
|
+
*
|
|
1376
|
+
* @example
|
|
1377
|
+
* ```ts
|
|
1378
|
+
* // Wait until at least one LOD starts loading before resolving
|
|
1379
|
+
* await lodsManager.awaitLoading({ waitForFirstCapture: true });
|
|
1380
|
+
* ```
|
|
1075
1381
|
*/
|
|
1076
1382
|
awaitLoading(t) {
|
|
1077
|
-
const e = this._promiseGroupIds++, s = new
|
|
1383
|
+
const e = this._promiseGroupIds++, s = new oe(this.#r, { ...t });
|
|
1078
1384
|
this._newPromiseGroups.push(s);
|
|
1079
1385
|
const r = performance.now();
|
|
1080
1386
|
return s.ready.finally(() => {
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1387
|
+
const n = this._newPromiseGroups.indexOf(s);
|
|
1388
|
+
n >= 0 && (this._newPromiseGroups.splice(n, 1), Fe() && performance.measure("LODsManager:awaitLoading", {
|
|
1083
1389
|
start: r,
|
|
1084
1390
|
detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
|
|
1085
1391
|
}));
|
|
1086
1392
|
}), s.ready;
|
|
1087
1393
|
}
|
|
1394
|
+
/** Track LOD work started outside this manager so {@link awaitLoading} waits for it too. */
|
|
1395
|
+
trackLoadingPromise(t, e, s) {
|
|
1396
|
+
return oe.addPromise(t, e, s, this._newPromiseGroups), s;
|
|
1397
|
+
}
|
|
1088
1398
|
_postprocessPromiseGroups() {
|
|
1089
1399
|
if (this._newPromiseGroups.length !== 0)
|
|
1090
1400
|
for (let t = this._newPromiseGroups.length - 1; t >= 0; t--)
|
|
1091
1401
|
this._newPromiseGroups[t].update(this.#r);
|
|
1092
1402
|
}
|
|
1093
1403
|
_lodchangedlisteners = [];
|
|
1404
|
+
/**
|
|
1405
|
+
* Register a listener that is called whenever a mesh or texture LOD level has finished loading and has been applied.
|
|
1406
|
+
* The listener receives the type of asset (`"mesh"` or `"texture"`), the new LOD level, and the affected object.
|
|
1407
|
+
*
|
|
1408
|
+
* @param evt - The event type. Currently only `"changed"` is supported.
|
|
1409
|
+
* @param listener - Callback invoked after a LOD swap completes.
|
|
1410
|
+
* @return A function to unregister the listener.
|
|
1411
|
+
*
|
|
1412
|
+
* @example
|
|
1413
|
+
* ```ts
|
|
1414
|
+
* lodsManager.addEventListener("changed", ({ type, level, object }) => {
|
|
1415
|
+
* console.log(`${type} LOD changed to level ${level}`, object);
|
|
1416
|
+
* });
|
|
1417
|
+
* ```
|
|
1418
|
+
*/
|
|
1094
1419
|
addEventListener(t, e) {
|
|
1095
|
-
t === "changed"
|
|
1420
|
+
return t === "changed" ? (this._lodchangedlisteners.push(e), () => {
|
|
1421
|
+
this.removeEventListener(t, e);
|
|
1422
|
+
}) : () => {
|
|
1423
|
+
};
|
|
1096
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Remove a previously registered `"changed"` event listener.
|
|
1427
|
+
* @param evt - The event type (`"changed"`).
|
|
1428
|
+
* @param listener - The listener to remove.
|
|
1429
|
+
* @return `true` if the listener was found and removed, `false` otherwise.
|
|
1430
|
+
*/
|
|
1097
1431
|
removeEventListener(t, e) {
|
|
1432
|
+
let s = !1;
|
|
1098
1433
|
if (t === "changed") {
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1434
|
+
const r = this._lodchangedlisteners.indexOf(e);
|
|
1435
|
+
r >= 0 && (this._lodchangedlisteners.splice(r, 1), s = !0);
|
|
1101
1436
|
}
|
|
1437
|
+
return s;
|
|
1102
1438
|
}
|
|
1103
1439
|
// readonly plugins: NEEDLE_progressive_plugin[] = [];
|
|
1104
1440
|
constructor(t, e) {
|
|
1105
1441
|
this.renderer = t, this.context = { ...e };
|
|
1106
1442
|
}
|
|
1107
1443
|
#t;
|
|
1108
|
-
#n = new Ze();
|
|
1109
1444
|
#r = 0;
|
|
1445
|
+
#n = 0;
|
|
1110
1446
|
#o = 0;
|
|
1111
|
-
#i = 0;
|
|
1112
1447
|
#s = 0;
|
|
1448
|
+
#i = Bt();
|
|
1113
1449
|
_fpsBuffer = [60, 60, 60, 60, 60];
|
|
1114
1450
|
/**
|
|
1115
1451
|
* Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
|
|
@@ -1120,29 +1456,46 @@ class v {
|
|
|
1120
1456
|
let t = 0;
|
|
1121
1457
|
this.#t = this.renderer.render;
|
|
1122
1458
|
const e = this;
|
|
1123
|
-
|
|
1124
|
-
const
|
|
1125
|
-
(
|
|
1126
|
-
const
|
|
1127
|
-
e.#t.call(this, s, r), e.onAfterRender(s, r,
|
|
1459
|
+
_e(this.renderer), this.renderer.render = function(s, r) {
|
|
1460
|
+
const n = e.renderer.getRenderTarget();
|
|
1461
|
+
(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((a, l) => a + l) / e._fpsBuffer.length, A && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
|
|
1462
|
+
const o = t++;
|
|
1463
|
+
e.#t.call(this, s, r), e.onAfterRender(s, r, o);
|
|
1128
1464
|
};
|
|
1129
1465
|
}
|
|
1130
1466
|
disable() {
|
|
1131
1467
|
this.#t && (console.debug("[gltf-progressive] Disabling LODsManager for renderer"), this.renderer.render = this.#t, this.#t = void 0);
|
|
1132
1468
|
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Manually trigger a LOD update for a scene and camera.
|
|
1471
|
+
* Only needed when {@link manual} is set to `true` — otherwise LOD updates happen automatically on each render call.
|
|
1472
|
+
*
|
|
1473
|
+
* @param scene - The scene containing objects with progressive LODs.
|
|
1474
|
+
* @param camera - The camera used to determine screen coverage and LOD levels.
|
|
1475
|
+
*
|
|
1476
|
+
* @example
|
|
1477
|
+
* ```ts
|
|
1478
|
+
* const lodsManager = LODsManager.get(renderer);
|
|
1479
|
+
* lodsManager.manual = true;
|
|
1480
|
+
* // ... later, trigger an update at a specific point:
|
|
1481
|
+
* lodsManager.update(scene, camera);
|
|
1482
|
+
* ```
|
|
1483
|
+
*/
|
|
1133
1484
|
update(t, e) {
|
|
1134
1485
|
this.internalUpdate(t, e);
|
|
1135
1486
|
}
|
|
1136
1487
|
onAfterRender(t, e, s) {
|
|
1137
1488
|
if (this.pause) return;
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1489
|
+
const r = this.getRenderList(t, e, s);
|
|
1490
|
+
if (!r) return;
|
|
1491
|
+
const n = r.opaque;
|
|
1492
|
+
let o = !0;
|
|
1493
|
+
if (n.length === 1) {
|
|
1494
|
+
const a = n[0].material;
|
|
1495
|
+
(a.name === "EffectMaterial" || a.name === "CopyShader") && (o = !1);
|
|
1143
1496
|
}
|
|
1144
|
-
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (
|
|
1145
|
-
if (
|
|
1497
|
+
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (o = !1), o) {
|
|
1498
|
+
if (It || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, A && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, A && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
|
|
1146
1499
|
return;
|
|
1147
1500
|
this.internalUpdate(t, e), this._postprocessPromiseGroups();
|
|
1148
1501
|
}
|
|
@@ -1151,15 +1504,17 @@ class v {
|
|
|
1151
1504
|
* Update LODs in a scene
|
|
1152
1505
|
*/
|
|
1153
1506
|
internalUpdate(t, e) {
|
|
1154
|
-
const s = this.
|
|
1507
|
+
const s = this.getRenderList(t, e, 0);
|
|
1508
|
+
if (!s) return;
|
|
1509
|
+
const r = s.opaque;
|
|
1155
1510
|
this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix, e.matrixWorldInverse);
|
|
1156
|
-
const
|
|
1157
|
-
for (const
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1511
|
+
const n = this.targetTriangleDensity;
|
|
1512
|
+
for (const l of r) {
|
|
1513
|
+
if (l.material && (l.geometry?.type === "BoxGeometry" || l.geometry?.type === "BufferGeometry") && (l.material.name === "SphericalGaussianBlur" || l.material.name == "BackgroundCubeMaterial" || l.material.name === "CubemapFromEquirect" || l.material.name === "EquirectangularToCubeUV")) {
|
|
1514
|
+
A && (l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] || (l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] = !0, console.warn("Ignoring skybox or BLIT object", l, l.material.name, l.material.type)));
|
|
1160
1515
|
continue;
|
|
1161
1516
|
}
|
|
1162
|
-
switch (
|
|
1517
|
+
switch (l.material.type) {
|
|
1163
1518
|
case "LineBasicMaterial":
|
|
1164
1519
|
case "LineDashedMaterial":
|
|
1165
1520
|
case "PointsMaterial":
|
|
@@ -1168,38 +1523,52 @@ class v {
|
|
|
1168
1523
|
case "MeshDepthMaterial":
|
|
1169
1524
|
continue;
|
|
1170
1525
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const c = Math.random() * 16777215, y = new et({ color: c });
|
|
1174
|
-
a.object.material = y;
|
|
1175
|
-
}
|
|
1176
|
-
const u = a.object;
|
|
1177
|
-
(u instanceof j || u.isMesh) && this.updateLODs(t, e, u, o);
|
|
1526
|
+
const u = l.object;
|
|
1527
|
+
(u instanceof K || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1178
1528
|
}
|
|
1179
|
-
const
|
|
1180
|
-
for (const
|
|
1181
|
-
const u =
|
|
1182
|
-
(u instanceof
|
|
1529
|
+
const o = s.transparent;
|
|
1530
|
+
for (const l of o) {
|
|
1531
|
+
const u = l.object;
|
|
1532
|
+
(u instanceof K || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1183
1533
|
}
|
|
1184
|
-
const
|
|
1185
|
-
for (const
|
|
1186
|
-
const u =
|
|
1187
|
-
(u instanceof
|
|
1534
|
+
const a = s.transmissive;
|
|
1535
|
+
for (const l of a) {
|
|
1536
|
+
const u = l.object;
|
|
1537
|
+
(u instanceof K || u.isMesh) && this.updateLODs(t, e, u, n);
|
|
1188
1538
|
}
|
|
1189
1539
|
}
|
|
1540
|
+
getRenderList(t, e, s) {
|
|
1541
|
+
const r = this.renderer;
|
|
1542
|
+
let n = null;
|
|
1543
|
+
if (r.isWebGPURenderer === !0) {
|
|
1544
|
+
const o = r._renderLists;
|
|
1545
|
+
if (!o) return null;
|
|
1546
|
+
n = o.get(t, e);
|
|
1547
|
+
} else if (r.isWebGLRenderer === !0) {
|
|
1548
|
+
const o = r.renderLists;
|
|
1549
|
+
if (!o) return null;
|
|
1550
|
+
n = o.get(t, s);
|
|
1551
|
+
}
|
|
1552
|
+
return n ? {
|
|
1553
|
+
opaque: n.opaque || [],
|
|
1554
|
+
transparent: n.transparent || [],
|
|
1555
|
+
transmissive: n.transmissive || n.transparentDoublePass || [],
|
|
1556
|
+
transparentDoublePass: n.transparentDoublePass || []
|
|
1557
|
+
} : null;
|
|
1558
|
+
}
|
|
1190
1559
|
/** Update the LOD levels for the renderer. */
|
|
1191
1560
|
updateLODs(t, e, s, r) {
|
|
1192
1561
|
s.userData || (s.userData = {});
|
|
1193
|
-
let
|
|
1194
|
-
if (
|
|
1562
|
+
let n = s[ge];
|
|
1563
|
+
if (n || (n = new zt(), s[ge] = n), n.frames++ < 2)
|
|
1195
1564
|
return;
|
|
1196
|
-
for (const
|
|
1197
|
-
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
for (const
|
|
1201
|
-
|
|
1202
|
-
|
|
1565
|
+
for (const a of F)
|
|
1566
|
+
a.onBeforeUpdateLOD?.(this.renderer, t, e, s);
|
|
1567
|
+
const o = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : X;
|
|
1568
|
+
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), h && s.material && !s.isGizmo && Ue(s.material), At && s.material && !s.isGizmo && !s.isBatchedMesh && ze(s.material, R.mesh_lod);
|
|
1569
|
+
for (const a of F)
|
|
1570
|
+
a.onAfterUpdatedLOD?.(this.renderer, t, e, s, R);
|
|
1571
|
+
n.lastLodLevel_Mesh = R.mesh_lod, n.lastLodLevel_Texture = R.texture_lod;
|
|
1203
1572
|
}
|
|
1204
1573
|
/** Load progressive textures for the given material
|
|
1205
1574
|
* @param material the material to load the textures for
|
|
@@ -1210,16 +1579,18 @@ class v {
|
|
|
1210
1579
|
if (!t) return;
|
|
1211
1580
|
if (Array.isArray(t)) {
|
|
1212
1581
|
for (const o of t)
|
|
1213
|
-
this.loadProgressiveTextures(o, e);
|
|
1582
|
+
this.loadProgressiveTextures(o, e, s);
|
|
1214
1583
|
return;
|
|
1215
1584
|
}
|
|
1216
1585
|
let r = !1;
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1586
|
+
(t[E] === void 0 || e < t[E]) && (r = !0);
|
|
1587
|
+
const n = s !== void 0 && s >= 0;
|
|
1588
|
+
if (n && (r = t[E] != s, e = s), r) {
|
|
1589
|
+
t[E] = e;
|
|
1590
|
+
const o = n ? { force: !0 } : void 0, a = g.assignTextureLOD(t, e, o).then((l) => {
|
|
1591
|
+
this._lodchangedlisteners.forEach((u) => u({ type: "texture", level: e, object: t }));
|
|
1221
1592
|
});
|
|
1222
|
-
|
|
1593
|
+
oe.addPromise("texture", t, a, this._newPromiseGroups);
|
|
1223
1594
|
}
|
|
1224
1595
|
}
|
|
1225
1596
|
/** Load progressive meshes for the given mesh
|
|
@@ -1230,143 +1601,110 @@ class v {
|
|
|
1230
1601
|
*/
|
|
1231
1602
|
loadProgressiveMeshes(t, e) {
|
|
1232
1603
|
if (!t) return Promise.resolve(null);
|
|
1233
|
-
let s = t[
|
|
1604
|
+
let s = t[E] !== e;
|
|
1234
1605
|
const r = t["DEBUG:LOD"];
|
|
1235
|
-
if (r != null && (s = t[
|
|
1236
|
-
t[
|
|
1237
|
-
const
|
|
1238
|
-
return
|
|
1606
|
+
if (r != null && (s = t[E] != r, e = r), s) {
|
|
1607
|
+
t[E] = e;
|
|
1608
|
+
const n = t.geometry, o = g.assignMeshLOD(t, e).then((a) => (a && t[E] == e && n != t.geometry && this._lodchangedlisteners.forEach((l) => l({ type: "mesh", level: e, object: t })), a));
|
|
1609
|
+
return oe.addPromise("mesh", t, o, this._newPromiseGroups), o;
|
|
1239
1610
|
}
|
|
1240
1611
|
return Promise.resolve(null);
|
|
1241
1612
|
}
|
|
1242
1613
|
// private testIfLODLevelsAreAvailable() {
|
|
1243
|
-
_sphere = new
|
|
1244
|
-
|
|
1245
|
-
_tempBox2 = new ge();
|
|
1246
|
-
tempMatrix = new Me();
|
|
1247
|
-
_tempWorldPosition = new A();
|
|
1248
|
-
_tempBoxSize = new A();
|
|
1249
|
-
_tempBox2Size = new A();
|
|
1250
|
-
static corner0 = new A();
|
|
1251
|
-
static corner1 = new A();
|
|
1252
|
-
static corner2 = new A();
|
|
1253
|
-
static corner3 = new A();
|
|
1254
|
-
static _tempPtInside = new A();
|
|
1255
|
-
static isInside(t, e) {
|
|
1256
|
-
const s = t.min, r = t.max, o = (s.x + r.x) * 0.5, n = (s.y + r.y) * 0.5;
|
|
1257
|
-
return this._tempPtInside.set(o, n, s.z).applyMatrix4(e).z < 0;
|
|
1258
|
-
}
|
|
1614
|
+
_sphere = new Ae();
|
|
1615
|
+
_tempWorldPosition = new k();
|
|
1259
1616
|
static skinnedMeshBoundsFrameOffsetCounter = 0;
|
|
1260
1617
|
static $skinnedMeshBoundsOffset = /* @__PURE__ */ Symbol("gltf-progressive-skinnedMeshBoundsOffset");
|
|
1261
1618
|
// #region calculateLodLevel
|
|
1262
|
-
calculateLodLevel(t, e, s, r,
|
|
1619
|
+
calculateLodLevel(t, e, s, r, n) {
|
|
1263
1620
|
if (!e) {
|
|
1264
|
-
|
|
1621
|
+
n.mesh_lod = -1, n.texture_lod = -1;
|
|
1265
1622
|
return;
|
|
1266
1623
|
}
|
|
1267
1624
|
if (!t) {
|
|
1268
|
-
|
|
1625
|
+
n.mesh_lod = -1, n.texture_lod = -1;
|
|
1269
1626
|
return;
|
|
1270
1627
|
}
|
|
1271
|
-
let
|
|
1272
|
-
if (
|
|
1628
|
+
let a = 10 + 1, l = !1;
|
|
1629
|
+
if (A && e["DEBUG:LOD"] != null)
|
|
1273
1630
|
return e["DEBUG:LOD"];
|
|
1274
|
-
const u =
|
|
1275
|
-
if (!
|
|
1276
|
-
|
|
1631
|
+
const u = g.getMeshLODExtension(e.geometry)?.lods, f = g.getPrimitiveIndex(e.geometry), c = u && u.length > 0, d = g.getMaterialMinMaxLODsCount(e.material), p = d.min_count !== 1 / 0 && d.min_count >= 0 && d.max_count >= 0;
|
|
1632
|
+
if (!c && !p) {
|
|
1633
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1277
1634
|
return;
|
|
1278
1635
|
}
|
|
1279
|
-
|
|
1280
|
-
const
|
|
1281
|
-
let
|
|
1636
|
+
c || (l = !0, a = 0);
|
|
1637
|
+
const b = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
|
|
1638
|
+
let y = e.geometry.boundingBox;
|
|
1282
1639
|
if (e.type === "SkinnedMesh") {
|
|
1283
|
-
const
|
|
1284
|
-
if (!
|
|
1285
|
-
|
|
1640
|
+
const x = e;
|
|
1641
|
+
if (!x.boundingBox)
|
|
1642
|
+
x.computeBoundingBox();
|
|
1286
1643
|
else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
|
|
1287
|
-
if (!
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1644
|
+
if (!x[I.$skinnedMeshBoundsOffset]) {
|
|
1645
|
+
const w = I.skinnedMeshBoundsFrameOffsetCounter++;
|
|
1646
|
+
x[I.$skinnedMeshBoundsOffset] = w;
|
|
1290
1647
|
}
|
|
1291
|
-
const
|
|
1292
|
-
if ((s.frames +
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1648
|
+
const L = x[I.$skinnedMeshBoundsOffset];
|
|
1649
|
+
if ((s.frames + L) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
|
|
1650
|
+
const w = ne(x), z = x.geometry;
|
|
1651
|
+
w && (x.geometry = w), x.computeBoundingBox(), x.geometry = z;
|
|
1295
1652
|
}
|
|
1296
1653
|
}
|
|
1297
|
-
|
|
1654
|
+
y = x.boundingBox;
|
|
1298
1655
|
}
|
|
1299
|
-
if (
|
|
1300
|
-
const d = t;
|
|
1656
|
+
if (y) {
|
|
1301
1657
|
if (e.geometry.attributes.color && e.geometry.attributes.color.count < 100 && e.geometry.boundingSphere) {
|
|
1302
1658
|
this._sphere.copy(e.geometry.boundingSphere), this._sphere.applyMatrix4(e.matrixWorld);
|
|
1303
|
-
const
|
|
1304
|
-
if (this._sphere.containsPoint(
|
|
1305
|
-
|
|
1659
|
+
const w = t.getWorldPosition(this._tempWorldPosition);
|
|
1660
|
+
if (this._sphere.containsPoint(w)) {
|
|
1661
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1306
1662
|
return;
|
|
1307
1663
|
}
|
|
1308
1664
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1665
|
+
const x = Ut({
|
|
1666
|
+
geometry: e.geometry,
|
|
1667
|
+
matrixWorld: e.matrixWorld,
|
|
1668
|
+
camera: t,
|
|
1669
|
+
projectionScreenMatrix: this.projectionScreenMatrix,
|
|
1670
|
+
desiredDensity: r,
|
|
1671
|
+
canvasHeight: b,
|
|
1672
|
+
currentLevel: s.lastLodLevel_Mesh,
|
|
1673
|
+
boundingBox: y,
|
|
1674
|
+
xrEnabled: this.renderer.xr.enabled,
|
|
1675
|
+
debugDrawLine: A ? I.debugDrawLine : void 0,
|
|
1676
|
+
warnMissingPrimitiveDensities: !0
|
|
1677
|
+
});
|
|
1678
|
+
if (s.lastCentrality = x.centrality, s.lastScreenCoverage = x.screenCoverage, s.lastScreenspaceVolume.copy(x.screenspaceVolume), x.screenCoverage === 1 / 0) {
|
|
1679
|
+
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1311
1680
|
return;
|
|
1312
1681
|
}
|
|
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);
|
|
1682
|
+
x.level >= 0 && x.level < a && (a = x.level, l = !0);
|
|
1345
1683
|
}
|
|
1346
|
-
if (
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1684
|
+
if (l ? n.mesh_lod = a : n.mesh_lod = s.lastLodLevel_Mesh, A && n.mesh_lod != s.lastLodLevel_Mesh) {
|
|
1685
|
+
const L = u?.[n.mesh_lod];
|
|
1686
|
+
L && console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${L.densities?.[f].toFixed(0)}) | ${e.name}`);
|
|
1349
1687
|
}
|
|
1350
|
-
if (
|
|
1351
|
-
const
|
|
1688
|
+
if (p) {
|
|
1689
|
+
const x = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1352
1690
|
if (s.lastLodLevel_Texture < 0) {
|
|
1353
|
-
if (
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1691
|
+
if (n.texture_lod = d.max_count - 1, A) {
|
|
1692
|
+
const L = d.lods[d.max_count - 1];
|
|
1693
|
+
A && console.log(`First Texture LOD ${n.texture_lod} (${L.max_height}px) - ${e.name}`);
|
|
1356
1694
|
}
|
|
1357
1695
|
} 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: ${
|
|
1696
|
+
const L = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
|
|
1697
|
+
let w = s.lastScreenCoverage * 4;
|
|
1698
|
+
this.context?.engine === "model-viewer" && (w *= 1.5);
|
|
1699
|
+
const z = this.renderer.getPixelRatio?.() || globalThis.devicePixelRatio || 1, T = b / z * w;
|
|
1700
|
+
let O = !1;
|
|
1701
|
+
for (let P = d.lods.length - 1; P >= 0; P--) {
|
|
1702
|
+
const S = d.lods[P];
|
|
1703
|
+
if (!(x && S.max_height >= 2048) && !(Me() && S.max_height > 4096) && (S.max_height > T || !O && P === 0)) {
|
|
1704
|
+
if (O = !0, n.texture_lod = P, A && n.texture_lod < s.lastLodLevel_Texture) {
|
|
1705
|
+
const _ = S.max_height;
|
|
1706
|
+
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${_}px
|
|
1707
|
+
Screensize: ${T.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${L.toFixed(1)}
|
|
1370
1708
|
${e.name}`);
|
|
1371
1709
|
}
|
|
1372
1710
|
break;
|
|
@@ -1374,62 +1712,77 @@ ${e.name}`);
|
|
|
1374
1712
|
}
|
|
1375
1713
|
}
|
|
1376
1714
|
} else
|
|
1377
|
-
|
|
1715
|
+
n.texture_lod = 0;
|
|
1378
1716
|
}
|
|
1379
1717
|
}
|
|
1380
|
-
class
|
|
1718
|
+
class zt {
|
|
1381
1719
|
frames = 0;
|
|
1382
1720
|
lastLodLevel_Mesh = -1;
|
|
1383
1721
|
lastLodLevel_Texture = -1;
|
|
1384
1722
|
lastScreenCoverage = 0;
|
|
1385
|
-
lastScreenspaceVolume = new
|
|
1723
|
+
lastScreenspaceVolume = new k();
|
|
1386
1724
|
lastCentrality = 0;
|
|
1387
1725
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1726
|
+
function ze(i, t) {
|
|
1727
|
+
if (!(t < 0)) {
|
|
1728
|
+
if (Array.isArray(i)) {
|
|
1729
|
+
for (const e of i)
|
|
1730
|
+
ze(e, t);
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
"color" in i && i.color instanceof Ie && (i.color.copy(Et(t, $t)), i.needsUpdate = !0);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
function Et(i, t) {
|
|
1737
|
+
const e = Math.max(0, Math.min(Se.length - 1, Math.floor(i)));
|
|
1738
|
+
return t.setHex(Se[e]);
|
|
1739
|
+
}
|
|
1740
|
+
const Pe = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ae = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
|
|
1741
|
+
let me = null;
|
|
1742
|
+
function Ee() {
|
|
1743
|
+
const i = Nt();
|
|
1392
1744
|
i && (i.mapURLs(function(t) {
|
|
1393
|
-
return
|
|
1394
|
-
}),
|
|
1745
|
+
return Ce(), t;
|
|
1746
|
+
}), Ce(), me?.disconnect(), me = new MutationObserver((t) => {
|
|
1395
1747
|
t.forEach((e) => {
|
|
1396
1748
|
e.addedNodes.forEach((s) => {
|
|
1397
|
-
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" &&
|
|
1749
|
+
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ne(s);
|
|
1398
1750
|
});
|
|
1399
1751
|
});
|
|
1400
|
-
}),
|
|
1752
|
+
}), me.observe(document, { childList: !0, subtree: !0 }));
|
|
1401
1753
|
}
|
|
1402
|
-
function
|
|
1754
|
+
function Nt() {
|
|
1403
1755
|
if (typeof customElements > "u") return null;
|
|
1404
1756
|
const i = customElements.get("model-viewer");
|
|
1405
1757
|
return i || (customElements.whenDefined("model-viewer").then(() => {
|
|
1406
|
-
console.debug("[gltf-progressive] model-viewer defined"),
|
|
1758
|
+
console.debug("[gltf-progressive] model-viewer defined"), Ee();
|
|
1407
1759
|
}), null);
|
|
1408
1760
|
}
|
|
1409
|
-
function
|
|
1761
|
+
function Ce() {
|
|
1410
1762
|
if (typeof document > "u") return;
|
|
1411
1763
|
document.querySelectorAll("model-viewer").forEach((t) => {
|
|
1412
|
-
|
|
1764
|
+
Ne(t);
|
|
1413
1765
|
});
|
|
1414
1766
|
}
|
|
1415
|
-
const
|
|
1416
|
-
let
|
|
1417
|
-
function
|
|
1418
|
-
if (!i ||
|
|
1767
|
+
const ke = /* @__PURE__ */ new WeakSet();
|
|
1768
|
+
let Vt = 0;
|
|
1769
|
+
function Ne(i) {
|
|
1770
|
+
if (!i || ke.has(i))
|
|
1419
1771
|
return null;
|
|
1420
|
-
|
|
1772
|
+
ke.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++Vt + `
|
|
1421
1773
|
`, i.getAttribute("src"));
|
|
1422
1774
|
let t = null, e = null, s = null;
|
|
1423
1775
|
for (let r = i; r != null; r = Object.getPrototypeOf(r)) {
|
|
1424
|
-
const
|
|
1425
|
-
!t &&
|
|
1776
|
+
const n = Object.getOwnPropertySymbols(r), o = n.find((u) => u.toString() == "Symbol(renderer)"), a = n.find((u) => u.toString() == "Symbol(scene)"), l = n.find((u) => u.toString() == "Symbol(needsRender)");
|
|
1777
|
+
!t && o != null && (t = i[o].threeRenderer), !e && a != null && (e = i[a]), !s && l != null && (s = i[l]);
|
|
1426
1778
|
}
|
|
1427
1779
|
if (t && e) {
|
|
1428
1780
|
let r = function() {
|
|
1429
1781
|
if (s) {
|
|
1430
|
-
let
|
|
1431
|
-
|
|
1432
|
-
|
|
1782
|
+
let o = 0;
|
|
1783
|
+
const a = setInterval(() => {
|
|
1784
|
+
if (o++ > 5) {
|
|
1785
|
+
clearInterval(a);
|
|
1433
1786
|
return;
|
|
1434
1787
|
}
|
|
1435
1788
|
s?.call(i);
|
|
@@ -1437,20 +1790,20 @@ function Ge(i) {
|
|
|
1437
1790
|
}
|
|
1438
1791
|
};
|
|
1439
1792
|
console.debug("[gltf-progressive] setup model-viewer");
|
|
1440
|
-
const
|
|
1441
|
-
return
|
|
1793
|
+
const n = I.get(t, { engine: "model-viewer" });
|
|
1794
|
+
return I.addPlugin(new Xt()), n.enable(), n.addEventListener("changed", () => {
|
|
1442
1795
|
s?.call(i);
|
|
1443
|
-
}), i.addEventListener("model-visibility", (
|
|
1444
|
-
|
|
1796
|
+
}), i.addEventListener("model-visibility", (o) => {
|
|
1797
|
+
o.detail.visible && s?.call(i);
|
|
1445
1798
|
}), i.addEventListener("load", () => {
|
|
1446
1799
|
r();
|
|
1447
1800
|
}), () => {
|
|
1448
|
-
|
|
1801
|
+
n.disable();
|
|
1449
1802
|
};
|
|
1450
1803
|
}
|
|
1451
1804
|
return null;
|
|
1452
1805
|
}
|
|
1453
|
-
class
|
|
1806
|
+
class Xt {
|
|
1454
1807
|
_didWarnAboutMissingUrl = !1;
|
|
1455
1808
|
onBeforeUpdateLOD(t, e, s, r) {
|
|
1456
1809
|
this.tryParseMeshLOD(e, r), this.tryParseTextureLOD(e, r);
|
|
@@ -1468,50 +1821,50 @@ class At {
|
|
|
1468
1821
|
return t.element;
|
|
1469
1822
|
}
|
|
1470
1823
|
tryParseTextureLOD(t, e) {
|
|
1471
|
-
if (e[
|
|
1472
|
-
e[
|
|
1473
|
-
const s = this.tryGetCurrentGLTF(t), r = this.tryGetCurrentModelViewer(t),
|
|
1474
|
-
if (
|
|
1475
|
-
let
|
|
1476
|
-
if (
|
|
1477
|
-
|
|
1478
|
-
const u = Object.keys(
|
|
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 " +
|
|
1824
|
+
if (e[ae] == !0) return;
|
|
1825
|
+
e[ae] = !0;
|
|
1826
|
+
const s = this.tryGetCurrentGLTF(t), r = this.tryGetCurrentModelViewer(t), n = this.getUrl(r);
|
|
1827
|
+
if (n && s && e.material) {
|
|
1828
|
+
let o = function(l) {
|
|
1829
|
+
if (l[ae] == !0) return;
|
|
1830
|
+
l[ae] = !0, l.userData && (l.userData.LOD = -1);
|
|
1831
|
+
const u = Object.keys(l);
|
|
1832
|
+
for (let f = 0; f < u.length; f++) {
|
|
1833
|
+
const c = u[f], d = l[c];
|
|
1834
|
+
if (d?.isTexture === !0) {
|
|
1835
|
+
const p = d.userData?.associations?.textures;
|
|
1836
|
+
if (p == null) continue;
|
|
1837
|
+
const b = s.parser.json.textures[p];
|
|
1838
|
+
if (!b) {
|
|
1839
|
+
console.warn("Texture data not found for texture index " + p);
|
|
1487
1840
|
continue;
|
|
1488
1841
|
}
|
|
1489
|
-
if (
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1842
|
+
if (b?.extensions?.[U]) {
|
|
1843
|
+
const y = b.extensions[U];
|
|
1844
|
+
y && n && g.registerTexture(n, d, y.lods.length, p, y);
|
|
1492
1845
|
}
|
|
1493
1846
|
}
|
|
1494
1847
|
}
|
|
1495
1848
|
};
|
|
1496
|
-
const
|
|
1497
|
-
if (Array.isArray(
|
|
1498
|
-
else
|
|
1849
|
+
const a = e.material;
|
|
1850
|
+
if (Array.isArray(a)) for (const l of a) o(l);
|
|
1851
|
+
else o(a);
|
|
1499
1852
|
}
|
|
1500
1853
|
}
|
|
1501
1854
|
tryParseMeshLOD(t, e) {
|
|
1502
|
-
if (e[
|
|
1503
|
-
e[
|
|
1855
|
+
if (e[Pe] == !0) return;
|
|
1856
|
+
e[Pe] = !0;
|
|
1504
1857
|
const s = this.tryGetCurrentModelViewer(t), r = this.getUrl(s);
|
|
1505
1858
|
if (!r)
|
|
1506
1859
|
return;
|
|
1507
|
-
const
|
|
1508
|
-
if (
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1860
|
+
const n = e.userData?.gltfExtensions?.[U];
|
|
1861
|
+
if (n && r) {
|
|
1862
|
+
const o = e.uuid;
|
|
1863
|
+
g.registerMesh(r, o, e, 0, n.lods.length, n);
|
|
1511
1864
|
}
|
|
1512
1865
|
}
|
|
1513
1866
|
}
|
|
1514
|
-
function
|
|
1867
|
+
function jt(...i) {
|
|
1515
1868
|
let t, e, s, r;
|
|
1516
1869
|
switch (i.length) {
|
|
1517
1870
|
case 2:
|
|
@@ -1526,22 +1879,22 @@ function Rt(...i) {
|
|
|
1526
1879
|
default:
|
|
1527
1880
|
throw new Error("Invalid arguments");
|
|
1528
1881
|
}
|
|
1529
|
-
|
|
1882
|
+
_e(e), Ge(s), We(s, {
|
|
1530
1883
|
progressive: !0,
|
|
1531
1884
|
...r?.hints
|
|
1532
|
-
}), s.register((
|
|
1533
|
-
const
|
|
1534
|
-
return r?.enableLODsManager !== !1 &&
|
|
1885
|
+
}), s.register((o) => new g(o));
|
|
1886
|
+
const n = I.get(e);
|
|
1887
|
+
return r?.enableLODsManager !== !1 && n.enable(), n;
|
|
1535
1888
|
}
|
|
1536
|
-
|
|
1537
|
-
if (!
|
|
1889
|
+
Ee();
|
|
1890
|
+
if (!_t) {
|
|
1538
1891
|
const i = {
|
|
1539
1892
|
gltfProgressive: {
|
|
1540
|
-
useNeedleProgressive:
|
|
1541
|
-
LODsManager:
|
|
1542
|
-
configureLoader:
|
|
1543
|
-
getRaycastMesh:
|
|
1544
|
-
useRaycastMeshes:
|
|
1893
|
+
useNeedleProgressive: jt,
|
|
1894
|
+
LODsManager: I,
|
|
1895
|
+
configureLoader: We,
|
|
1896
|
+
getRaycastMesh: ne,
|
|
1897
|
+
useRaycastMeshes: bt
|
|
1545
1898
|
}
|
|
1546
1899
|
};
|
|
1547
1900
|
if (!globalThis.Needle)
|
|
@@ -1551,18 +1904,21 @@ if (!yt) {
|
|
|
1551
1904
|
globalThis.Needle[t] = i[t];
|
|
1552
1905
|
}
|
|
1553
1906
|
export {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1907
|
+
U as EXTENSION_NAME,
|
|
1908
|
+
I as LODsManager,
|
|
1909
|
+
g as NEEDLE_progressive,
|
|
1910
|
+
ut as VERSION,
|
|
1911
|
+
Ge as addDracoAndKTX2Loaders,
|
|
1912
|
+
Ut as calculateMeshLODLevel,
|
|
1913
|
+
We as configureLoader,
|
|
1914
|
+
_e as createLoaders,
|
|
1915
|
+
Et as getLODColor,
|
|
1916
|
+
ne as getRaycastMesh,
|
|
1917
|
+
Se as lodDebugColors,
|
|
1918
|
+
Ee as patchModelViewer,
|
|
1919
|
+
Mt as registerRaycastMesh,
|
|
1920
|
+
ht as setDracoDecoderLocation,
|
|
1921
|
+
gt as setKTX2TranscoderLocation,
|
|
1922
|
+
jt as useNeedleProgressive,
|
|
1923
|
+
bt as useRaycastMeshes
|
|
1568
1924
|
};
|