@needle-tools/gltf-progressive 3.4.0-next.dbcee85 → 3.5.0-canary.4d42385

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