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