@needle-tools/gltf-progressive 3.4.0-rc → 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,69 +1,69 @@
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 Le, 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
17
  }).catch((n) => {
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/");
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(n) {
27
- R = n, A && A[ge] != R ? (console.debug("Updating Draco decoder path to " + n), A[ge] = R, A.setDecoderPath(R), A.preload()) : console.debug("Setting Draco decoder path to " + n);
28
- }
29
26
  function Je(n) {
30
- X = n, $ && $.transcoderPath != X ? (console.debug("Updating KTX2 transcoder path to " + n), $.setTranscoderPath(X), $.init()) : console.debug("Setting KTX2 transcoder path to " + 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
+ }
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(n) {
33
- return Pe(), n ? $.detectSupport(n) : n !== 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(n) {
36
- n.dracoLoader || n.setDRACOLoader(A), n.ktx2Loader || n.setKTX2Loader($), n.meshoptDecoder || n.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(n, t) {
45
- let e = pe.get(n);
46
- e ? e = Object.assign(e, t) : e = t, pe.set(n, 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(...n) {
50
- const t = pe.get(this);
48
+ const et = xe.prototype.load;
49
+ function tt(...n) {
50
+ const t = me.get(this);
51
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, i = t?.usecase ? t.usecase : "default";
55
- r ? this.requestHeader.Accept = `*/*;progressive=allowed;usecase=${i}` : this.requestHeader.Accept = `*/*;usecase=${i}`, 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 n[0] = e, Ze?.call(this, ...n);
57
+ return n[0] = e, et?.call(this, ...n);
58
58
  }
59
- ye.prototype.load = et;
60
- j("debugprogressive");
61
- function j(n) {
59
+ xe.prototype.load = tt;
60
+ q("debugprogressive");
61
+ function q(n) {
62
62
  if (typeof window > "u") return !1;
63
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(n, t) {
66
+ function st(n, t) {
67
67
  if (t === void 0 || n === void 0 || t.startsWith("./") || t.startsWith("http") || t.startsWith("data:") || t.startsWith("blob:"))
68
68
  return t;
69
69
  const e = n.lastIndexOf("/");
@@ -74,18 +74,18 @@ function tt(n, 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;
79
- }
80
- let K;
81
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
+ }
80
+ let H;
81
+ function be() {
82
82
  if (typeof window > "u") return !1;
83
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
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(n) {
122
- return n?.[me] instanceof E ? n[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;
123
131
  }
124
- function ot(n, t) {
125
- if ((n.type === "Mesh" || n.type === "SkinnedMesh") && !Z(n)) {
126
- const s = nt(t);
127
- s.userData = { isRaycastMesh: !0 }, n[me] = s;
132
+ const nt = typeof window > "u" && typeof document > "u", ye = /* @__PURE__ */ Symbol("needle:raycast-mesh");
133
+ function ee(n) {
134
+ return n?.[ye] instanceof E ? n[ye] : null;
135
+ }
136
+ function at(n, t) {
137
+ if ((n.type === "Mesh" || n.type === "SkinnedMesh") && !ee(n)) {
138
+ const s = ut(t);
139
+ s.userData = { isRaycastMesh: !0 }, n[ye] = s;
128
140
  }
129
141
  }
130
- function it(n = !0) {
142
+ function lt(n = !0) {
131
143
  if (n) {
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 i;
137
- r && o.isMesh && (i = o.geometry, o.geometry = r), t.call(this, e, s), i && (o.geometry = i);
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 nt(n) {
156
+ let Q = null;
157
+ function ut(n) {
146
158
  const t = new E();
147
159
  for (const e in n.attributes)
148
160
  t.setAttribute(e, n.getAttribute(e));
149
161
  return t.setIndex(n.getIndex()), t;
150
162
  }
151
- const z = new Array(), g = j("debugprogressive");
152
- let J, q = -1;
153
- if (g) {
163
+ const z = new Array(), f = q("debugprogressive");
164
+ let Z, V = -1;
165
+ if (f) {
154
166
  let n = function() {
155
- q += 1, q >= t && (q = -1), console.log(`Toggle LOD level [${q}]`);
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" && n(), 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(n) {
164
- if (g && J !== void 0)
175
+ function ke(n) {
176
+ if (f && Z !== void 0)
165
177
  if (Array.isArray(n))
166
178
  for (const t of n)
167
- Ce(t);
168
- else n && "wireframe" in n && (n.wireframe = J === !0);
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 dt(n) {
174
- if (Q.length < lt) {
175
- const s = Q.length;
176
- g && console.warn(`[Worker] Creating new worker #${s}`);
177
- const o = we.createWorker(n || {});
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
- ut(o.result), r.resolve(o.result);
192
- const i = r.url;
193
- i.startsWith("blob:") && URL.revokeObjectURL(i);
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 () => {
218
- const { WebGLRenderer: d } = await import("three");
219
- return new d();
220
- })(), o = await this._webglRenderer);
221
- const l = xe(o).ktx2Loader.workerConfig;
227
+ const s = Qe();
228
+ let r = e?.renderer;
229
+ r || (this._webglRenderer ??= (async () => {
230
+ const { WebGLRenderer: u } = await import("three");
231
+ return new u();
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",
@@ -227,60 +239,60 @@ class we {
227
239
  ktx2TranscoderPath: s.ktx2TranscoderPath,
228
240
  ktx2LoaderConfig: l
229
241
  };
230
- return this._debug && console.debug("[Worker] Sending load request", a), this.worker.postMessage(a), new Promise((d) => {
242
+ return this._debug && console.debug("[Worker] Sending load request", a), this.worker.postMessage(a), new Promise((u) => {
231
243
  this._running.push({
232
244
  url: t.toString(),
233
- resolve: d
245
+ resolve: u
234
246
  });
235
247
  });
236
248
  }
237
249
  _debug = !1;
238
250
  }
239
- function ut(n) {
251
+ function ht(n) {
240
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], i = ae(r);
248
- s.setAttribute(o, i);
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 i = e.morphAttributes[o].map((l) => ae(l));
253
- s.morphAttributes[o] = i;
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
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, i = 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
294
  i,
295
+ o,
284
296
  e.format,
285
297
  e.type,
286
298
  e.mapping,
@@ -303,7 +315,7 @@ function ut(n) {
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;
@@ -312,32 +324,57 @@ function ut(n) {
312
324
  }
313
325
  return n;
314
326
  }
315
- function ae(n) {
327
+ function le(n) {
316
328
  let t = n;
317
329
  if ("isInterleavedBufferAttribute" in n && n.isInterleavedBufferAttribute) {
318
- const e = n.data, s = e.array, o = new Fe(s, e.stride);
319
- t = new We(o, n.itemSize, s.byteOffset, n.normalized), t.offset = n.offset;
320
- } else "isBufferAttribute" in n && n.isBufferAttribute && (t = new Ue(n.array, n.itemSize, n.normalized), t.usage = n.usage, t.gpuType = n.gpuType, t.updateRanges = n.updateRanges);
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 (g === "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
- const d = l.uniforms[a].value;
354
- d?.isTexture === !0 && i(d, e);
390
+ const u = l.uniforms[a].value;
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 && i(a, e);
396
+ a?.isTexture === !0 && o(a, e);
360
397
  }
361
398
  else
362
- g && console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);
363
- return t[o] = e, e;
364
- function i(l, a) {
365
- const d = s.getAssignedLODInformation(l);
366
- if (d) {
367
- const u = s.lodInfos.get(d.key);
368
- if (u && u.lods) {
369
- a.min_count = Math.min(a.min_count, u.lods.length), a.max_count = Math.max(a.max_count, u.lods.length);
370
- for (let y = 0; y < u.lods.length; y++) {
371
- const w = u.lods[y];
399
+ f && console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);
400
+ return t[r] = e, e;
401
+ function o(l, a) {
402
+ const u = s.getAssignedLODInformation(l);
403
+ if (u) {
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 i = t[r];
392
- if (i && i.isTexture && this.hasLODLevelAvailable(i, 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 i = o.index || 0;
435
- r = r[i];
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 : g && console.error("Invalid LOD geometry", r))), r;
438
- }).catch((r) => (console.error("Error loading mesh LOD", t, r), null));
439
- } else g && 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 i = this.assignTextureLOD(r, e);
450
- o.push(i);
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 i = new Array();
454
- for (const l of r)
455
- Array.isArray(l) && i.push(...l);
456
- return i;
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 i = s;
465
- for (const l of Object.keys(i.uniforms)) {
466
- const a = i.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 d = this.assignTextureLODForSlot(a, e, s, l).then((u) => (u && i.uniforms[l].value != u && (i.uniforms[l].value = u, i.uniformsNeedUpdate = !0), u));
469
- o.push(d), 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 i of Object.keys(s)) {
474
- const l = s[i];
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, i);
477
- o.push(a), r.push(i);
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((i) => {
517
+ return Promise.all(r).then((o) => {
481
518
  const l = new Array();
482
- for (let a = 0; a < i.length; a++) {
483
- const d = i[a], u = r[a];
484
- d && d.isTexture === !0 ? l.push({ material: s, slot: u, texture: d, level: e }) : l.push({ material: s, slot: u, 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 i = s[o];
503
- if (i && !g) {
504
- const l = this.getAssignedLODInformation(i);
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 g === "verbose" && console.warn("Assigned texture level is already higher: ", l.level, e, s, i, 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 && g && 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 g == "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
- g && 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 g && 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
- g && 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 i of this.parser.associations.keys())
556
- i.isTexture === !0 && this.parser.associations.get(i)?.textures === s && (r = !0, m.registerTexture(this.url, i, o.lods?.length, s, o));
557
- r || this.parser.getDependency("texture", s).then((i) => {
558
- i && m.registerTexture(this.url, i, 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 i = this.parser.associations.get(r);
569
- i?.meshes === s && m.registerMesh(this.url, o.guid, r, o.lods.length, i.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
- g && 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 (g) {
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 i = r.guid;
589
- m.assignLODInformation(t, e, i, s, o), m.lodInfos.set(i, r), m.lowresCache.set(i, 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, i) => {
659
+ static registerMesh = (t, e, s, r, i, o) => {
595
660
  const l = s.geometry;
596
661
  if (!l) {
597
- g && 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 = {}), g && console.log("> Progressive: register mesh " + s.name, { index: r, uuid: s.uuid }, i, s), m.assignLODInformation(t, l, e, o, r), m.lodInfos.set(e, i);
601
- let d = m.lowresCache.get(e)?.deref();
602
- d ? d.push(s.geometry) : d = [s.geometry], m.lowresCache.set(e, new WeakRef(d)), o > 0 && !Z(s) && ot(s, l);
603
- for (const u of z)
604
- u.onRegisteredNewMesh?.(s, i);
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);
666
+ let u = m.lowresCache.get(e)?.deref();
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,236 +682,275 @@ 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.previouslyLoaded)
627
- s.includes(t) && (this._disposeCacheEntry(o), this.previouslyLoaded.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
- for (const [, e] of this.previouslyLoaded)
708
+ for (const [, e] of this.cache)
641
709
  this._disposeCacheEntry(e);
642
- this.previouslyLoaded.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();
658
730
  /** cache of already loaded mesh lods. Uses WeakRef for single resources to allow garbage collection when unused. */
659
- static previouslyLoaded = /* @__PURE__ */ new Map();
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.
665
739
  * The held value is the cache key string used in `previouslyLoaded`.
666
740
  */
667
741
  static _resourceRegistry = new FinalizationRegistry((t) => {
668
- const e = m.previouslyLoaded.get(t);
669
- console.debug(`[gltf-progressive] FinalizationRegistry cleanup: Resource GC'd for ${t}.`), e instanceof WeakRef && (e.deref() || (m.previouslyLoaded.delete(t), g && console.log(`[gltf-progressive] Cache entry auto-cleaned (GC'd): ${t}`)));
742
+ const e = m.cache.get(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)")));
670
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
+ }
671
774
  static workers = [];
672
775
  static _workersIndex = 0;
673
776
  static async getOrLoadLOD(t, e) {
674
- const s = g == "verbose", o = this.getAssignedLODInformation(t);
675
- if (!o)
676
- return g && console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`, t), null;
677
- const r = o?.key;
678
- let i;
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;
679
782
  if (t.isTexture === !0) {
680
783
  const a = t;
681
- a.source && a.source[le] && (i = a.source[le]);
784
+ a.source && a.source[ue] && (o = a.source[ue]);
682
785
  }
683
- if (i || (i = m.lodInfos.get(r)), !i)
684
- g && 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);
685
788
  else {
686
789
  if (e > 0) {
687
- let u = !1;
688
- const y = Array.isArray(i.lods);
689
- if (y && e >= i.lods.length ? u = !0 : y || (u = !0), u) {
690
- 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);
691
794
  if (w) {
692
- const L = w.deref();
693
- if (L) return L;
694
- this.lowresCache.delete(r), g && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`);
795
+ const _ = w.deref();
796
+ if (_) return _;
797
+ this.lowresCache.delete(i), f && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${i}`);
695
798
  }
696
799
  return null;
697
800
  }
698
801
  }
699
- const a = Array.isArray(i.lods) ? i.lods[e]?.path : i.lods;
802
+ const a = Array.isArray(o.lods) ? o.lods[e]?.path : o.lods;
700
803
  if (!a)
701
- return g && !i["missing:uri"] && (i["missing:uri"] = !0, console.warn("Missing uri for progressive asset for LOD " + e, i)), null;
702
- const d = tt(o.url, a);
703
- if (d.endsWith(".glb") || d.endsWith(".gltf")) {
704
- if (!i.guid)
705
- return console.warn("missing pointer for glb/gltf texture", i), null;
706
- const u = d + "_" + i.guid, y = await this.queue.slot(d), w = this.previouslyLoaded.get(u);
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);
806
+ if (u.endsWith(".glb") || u.endsWith(".gltf")) {
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);
707
810
  if (w !== void 0)
708
- if (s && console.log(`LOD ${e} was already loading/loaded: ${u}`), w instanceof WeakRef) {
709
- const c = w.deref();
710
- if (c) {
711
- let h = c, b = !1;
712
- if (h instanceof F && t instanceof F ? h.image?.data || h.source?.data ? h = this.copySettings(t, h) : b = !0 : h instanceof E && t instanceof E && (h.attributes.position?.array || (b = !0)), !b)
713
- return h;
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;
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)
816
+ return g;
714
817
  }
715
- this.previouslyLoaded.delete(u), g && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`);
818
+ this.cache.delete(c), f && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${c}`);
716
819
  } else {
717
- let c = await w.catch((b) => (console.error(`Error loading LOD ${e} from ${d}
718
- `, b), null)), h = !1;
719
- if (c == null || (c instanceof F && t instanceof F ? c.image?.data || c.source?.data ? c = this.copySettings(t, c) : (h = !0, this.previouslyLoaded.delete(u)) : c instanceof E && t instanceof E && (c.attributes.position?.array || (h = !0, this.previouslyLoaded.delete(u)))), !h)
720
- return c;
820
+ let d = await w.catch((b) => (console.error(`Error loading LOD ${e} from ${u}
821
+ `, b), null)), g = !1;
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;
721
824
  }
722
825
  if (!y.use)
723
- return g && console.log(`LOD ${e} was aborted: ${d}`), null;
724
- const L = i, I = new Promise(async (c, h) => {
725
- if (ct) {
726
- const x = await (await dt({})).load(d);
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);
727
830
  if (x.textures.length > 0)
728
- for (const f of x.textures) {
729
- let p = f.texture;
730
- return m.assignLODInformation(o.url, p, r, e, void 0), t instanceof F && (p = this.copySettings(t, p)), p && (p.guid = L.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);
731
834
  }
732
835
  if (x.geometries.length > 0) {
733
- const f = new Array();
836
+ const h = new Array();
734
837
  for (const p of x.geometries) {
735
- const D = p.geometry;
736
- m.assignLODInformation(o.url, D, r, e, p.primitiveIndex), f.push(D);
838
+ const O = p.geometry;
839
+ m.assignLODInformation(r.url, O, i, e, p.primitiveIndex), h.push(O);
737
840
  }
738
- return c(f);
841
+ return d(h);
739
842
  }
740
- return c(null);
843
+ return d(null);
741
844
  }
742
- const b = new ye();
743
- Se(b), g && (await new Promise((_) => setTimeout(_, 1e3)), s && console.warn("Start loading (delayed) " + d, L.guid));
744
- let B = d;
745
- if (L && Array.isArray(L.lods)) {
746
- const _ = L.lods[e];
747
- _.hash && (B += "?v=" + _.hash);
845
+ const b = new xe();
846
+ Te(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
847
+ let B = u;
848
+ if (_ && Array.isArray(_.lods)) {
849
+ const L = _.lods[e];
850
+ L.hash && (B += "?v=" + L.hash);
748
851
  }
749
- const M = await b.loadAsync(B).catch((_) => (console.error(`Error loading LOD ${e} from ${d}
750
- `, _), c(null)));
751
- if (!M)
752
- return c(null);
753
- const N = M.parser;
754
- s && console.log("Loading finished " + d, L.guid);
852
+ const D = await b.loadAsync(B).catch((L) => (console.error(`Error loading LOD ${e} from ${u}
853
+ `, L), d(null)));
854
+ if (!D)
855
+ return d(null);
856
+ const N = D.parser;
857
+ s && console.log("Loading finished " + u, _.guid);
755
858
  let P = 0;
756
- if (M.parser.json.textures) {
757
- let _ = !1;
758
- for (const x of M.parser.json.textures) {
859
+ if (D.parser.json.textures) {
860
+ let L = !1;
861
+ for (const x of D.parser.json.textures) {
759
862
  if (x?.extensions) {
760
- const f = x?.extensions[W];
761
- if (f?.guid && f.guid === L.guid) {
762
- _ = !0;
863
+ const h = x?.extensions[W];
864
+ if (h?.guid && h.guid === _.guid) {
865
+ L = !0;
763
866
  break;
764
867
  }
765
868
  }
766
869
  P++;
767
870
  }
768
- if (_) {
871
+ if (L) {
769
872
  let x = await N.getDependency("texture", P);
770
- return x && m.assignLODInformation(o.url, x, r, e, void 0), s && console.log('change "' + t.name + '" → "' + x.name + '"', d, P, x, u), t instanceof F && (x = this.copySettings(t, x)), x && (x.guid = L.guid), c(x);
771
- } else g && console.warn("Could not find texture with guid", L.guid, M.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);
772
875
  }
773
- if (P = 0, M.parser.json.meshes) {
774
- let _ = !1;
775
- for (const x of M.parser.json.meshes) {
876
+ if (P = 0, D.parser.json.meshes) {
877
+ let L = !1;
878
+ for (const x of D.parser.json.meshes) {
776
879
  if (x?.extensions) {
777
- const f = x?.extensions[W];
778
- if (f?.guid && f.guid === L.guid) {
779
- _ = !0;
880
+ const h = x?.extensions[W];
881
+ if (h?.guid && h.guid === _.guid) {
882
+ L = !0;
780
883
  break;
781
884
  }
782
885
  }
783
886
  P++;
784
887
  }
785
- if (_) {
888
+ if (L) {
786
889
  const x = await N.getDependency("mesh", P);
787
- if (s && console.log(`Loaded Mesh "${x.name}"`, d, P, x, u), x.isMesh === !0) {
788
- const f = x.geometry;
789
- 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);
790
893
  } else {
791
- const f = new Array();
894
+ const h = new Array();
792
895
  for (let p = 0; p < x.children.length; p++) {
793
- const D = x.children[p];
794
- if (D.isMesh === !0) {
795
- const S = D.geometry;
796
- m.assignLODInformation(o.url, S, r, e, p), f.push(S);
896
+ const O = x.children[p];
897
+ if (O.isMesh === !0) {
898
+ const S = O.geometry;
899
+ m.assignLODInformation(r.url, S, i, e, p), h.push(S);
797
900
  }
798
901
  }
799
- return c(f);
902
+ return d(h);
800
903
  }
801
- } else g && console.warn("Could not find mesh with guid", L.guid, M.parser.json);
904
+ } else f && console.warn("Could not find mesh with guid", _.guid, D.parser.json);
802
905
  }
803
- return c(null);
906
+ return d(null);
804
907
  });
805
- this.previouslyLoaded.set(u, I), y.use(I);
806
- const O = await I;
807
- return O != null ? Array.isArray(O) ? this.previouslyLoaded.set(u, Promise.resolve(O)) : (this.previouslyLoaded.set(u, new WeakRef(O)), m._resourceRegistry.register(O, u)) : this.previouslyLoaded.set(u, Promise.resolve(null)), O;
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;
808
911
  } else if (t instanceof F) {
809
- s && console.log("Load texture from uri: " + d);
810
- const y = await new ze().loadAsync(d);
811
- return y ? (y.guid = i.guid, y.flipY = !1, y.needsUpdate = !0, y.colorSpace = t.colorSpace, s && console.log(i, y)) : g && console.warn("failed loading", d), y;
912
+ s && console.log("Load texture from uri: " + u);
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;
812
915
  }
813
916
  }
814
917
  return null;
815
918
  }
816
- static maxConcurrent = 50;
817
- static queue = new st(m.maxConcurrent, { debug: g != !1 });
818
- 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) {
819
924
  if (!e) return;
820
925
  e.userData || (e.userData = {});
821
- const i = new ht(t, s, o, r);
822
- e.userData.LODS = i, "source" in e && typeof e.source == "object" && (e.source.LODS = i);
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);
823
928
  }
824
929
  static getAssignedLODInformation(t) {
825
930
  return t ? t.userData?.LODS ? t.userData.LODS : "source" in t && t.source?.LODS ? t.source.LODS : null : null;
826
931
  }
827
932
  // private static readonly _copiedTextures: WeakMap<Texture, Texture> = new Map();
828
933
  static copySettings(t, e) {
829
- return e ? (g === "verbose" && console.debug(`Copy texture settings
934
+ return e ? (f === "verbose" && console.debug(`Copy texture settings
830
935
  `, t.uuid, `
831
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;
832
937
  }
833
938
  }
834
- class ht {
939
+ class pt {
835
940
  url;
836
941
  /** the key to lookup the LOD information */
837
942
  key;
838
943
  level;
839
944
  /** For multi objects (e.g. a group of meshes) this is the index of the object */
840
945
  index;
841
- constructor(t, e, s, o) {
842
- 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);
843
948
  }
844
949
  }
845
- class de {
846
- static addPromise = (t, e, s, o) => {
847
- o.forEach((r) => {
848
- 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);
849
954
  });
850
955
  };
851
956
  ready;
@@ -875,9 +980,9 @@ class de {
875
980
  _awaiting = [];
876
981
  _maxPromisesPerObject = 1;
877
982
  constructor(t, e) {
878
- const o = Math.max(e.frames ?? 2, 2);
879
- this._frame_start = e.waitForFirstCapture ? void 0 : t, this._frames_to_capture = o, this.ready = new Promise((r) => {
880
- 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;
881
986
  }), this.ready.finally(() => {
882
987
  this._resolved = !0, this._awaiting.length = 0;
883
988
  }), this._signal = e.signal, this._signal?.addEventListener("abort", () => {
@@ -891,18 +996,18 @@ class de {
891
996
  _seen = /* @__PURE__ */ new WeakMap();
892
997
  add(t, e, s) {
893
998
  if (this._resolved) {
894
- g && 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.");
895
1000
  return;
896
1001
  }
897
1002
  if (!(this._frame_start !== void 0 && this._currentFrame > this._frame_start + this._frames_to_capture)) {
898
1003
  if (this._maxPromisesPerObject >= 1)
899
1004
  if (this._seen.has(e)) {
900
- let o = this._seen.get(e);
901
- if (o >= this._maxPromisesPerObject) {
902
- g && 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.");
903
1008
  return;
904
1009
  }
905
- this._seen.set(e, o + 1);
1010
+ this._seen.set(e, r + 1);
906
1011
  } else
907
1012
  this._seen.set(e, 1);
908
1013
  this._awaiting.push(s), this._addedCount++, s.finally(() => {
@@ -918,7 +1023,7 @@ class de {
918
1023
  });
919
1024
  }
920
1025
  }
921
- const k = j("debugprogressive"), gt = j("noprogressive"), ue = /* @__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 };
922
1027
  class v {
923
1028
  /**
924
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.
@@ -926,7 +1031,7 @@ class v {
926
1031
  static debugDrawLine;
927
1032
  /** @internal */
928
1033
  static getObjectLODState(t) {
929
- return t[ce];
1034
+ return t[fe];
930
1035
  }
931
1036
  static addPlugin(t) {
932
1037
  z.push(t);
@@ -941,17 +1046,17 @@ class v {
941
1046
  * @returns The LODsManager instance.
942
1047
  */
943
1048
  static get(t, e) {
944
- if (t[ue])
945
- return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[ue];
1049
+ if (t[de])
1050
+ return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[de];
946
1051
  const s = new v(t, {
947
1052
  engine: "unknown",
948
1053
  ...e
949
1054
  });
950
- return t[ue] = s, s;
1055
+ return t[de] = s, s;
951
1056
  }
952
1057
  renderer;
953
1058
  context;
954
- projectionScreenMatrix = new Le();
1059
+ projectionScreenMatrix = new ve();
955
1060
  /** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
956
1061
  get plugins() {
957
1062
  return z;
@@ -992,16 +1097,40 @@ class v {
992
1097
  _newPromiseGroups = [];
993
1098
  _promiseGroupIds = 0;
994
1099
  /**
995
- * 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
+ * ```
996
1125
  */
997
1126
  awaitLoading(t) {
998
- const e = this._promiseGroupIds++, s = new de(this.#r, { ...t });
1127
+ const e = this._promiseGroupIds++, s = new ce(this.#r, { ...t });
999
1128
  this._newPromiseGroups.push(s);
1000
- const o = performance.now();
1129
+ const r = performance.now();
1001
1130
  return s.ready.finally(() => {
1002
- const r = this._newPromiseGroups.indexOf(s);
1003
- r >= 0 && (this._newPromiseGroups.splice(r, 1), _e() && performance.measure("LODsManager:awaitLoading", {
1004
- 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,
1005
1134
  detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
1006
1135
  }));
1007
1136
  }), s.ready;
@@ -1012,23 +1141,49 @@ class v {
1012
1141
  this._newPromiseGroups[t].update(this.#r);
1013
1142
  }
1014
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
+ */
1015
1159
  addEventListener(t, e) {
1016
- t === "changed" && this._lodchangedlisteners.push(e);
1160
+ return t === "changed" ? (this._lodchangedlisteners.push(e), () => {
1161
+ this.removeEventListener(t, e);
1162
+ }) : () => {
1163
+ };
1017
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
+ */
1018
1171
  removeEventListener(t, e) {
1172
+ let s = !1;
1019
1173
  if (t === "changed") {
1020
- const s = this._lodchangedlisteners.indexOf(e);
1021
- s >= 0 && this._lodchangedlisteners.splice(s, 1);
1174
+ const r = this._lodchangedlisteners.indexOf(e);
1175
+ r >= 0 && (this._lodchangedlisteners.splice(r, 1), s = !0);
1022
1176
  }
1177
+ return s;
1023
1178
  }
1024
1179
  // readonly plugins: NEEDLE_progressive_plugin[] = [];
1025
1180
  constructor(t, e) {
1026
1181
  this.renderer = t, this.context = { ...e };
1027
1182
  }
1028
1183
  #t;
1029
- #i = new Ne();
1184
+ #o = new Ne();
1030
1185
  #r = 0;
1031
- #o = 0;
1186
+ #i = 0;
1032
1187
  #n = 0;
1033
1188
  #s = 0;
1034
1189
  _fpsBuffer = [60, 60, 60, 60, 60];
@@ -1041,29 +1196,44 @@ class v {
1041
1196
  let t = 0;
1042
1197
  this.#t = this.renderer.render;
1043
1198
  const e = this;
1044
- xe(this.renderer), this.renderer.render = function(s, o) {
1045
- const r = e.renderer.getRenderTarget();
1046
- (r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#o = e.#i.getDelta(), e.#n += 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));
1047
- const i = t++;
1048
- e.#t.call(this, s, o), e.onAfterRender(s, o, i);
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);
1049
1204
  };
1050
1205
  }
1051
1206
  disable() {
1052
1207
  this.#t && (console.debug("[gltf-progressive] Disabling LODsManager for renderer"), this.renderer.render = this.#t, this.#t = void 0);
1053
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
+ */
1054
1224
  update(t, e) {
1055
1225
  this.internalUpdate(t, e);
1056
1226
  }
1057
1227
  onAfterRender(t, e, s) {
1058
1228
  if (this.pause) return;
1059
- const r = this.renderer.renderLists.get(t, 0).opaque;
1060
- let i = !0;
1061
- if (r.length === 1) {
1062
- const l = r[0].material;
1063
- (l.name === "EffectMaterial" || l.name === "CopyShader") && (i = !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);
1064
1234
  }
1065
- if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (i = !1), i) {
1066
- 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))
1067
1237
  return;
1068
1238
  this.internalUpdate(t, e), this._postprocessPromiseGroups();
1069
1239
  }
@@ -1072,12 +1242,12 @@ class v {
1072
1242
  * Update LODs in a scene
1073
1243
  */
1074
1244
  internalUpdate(t, e) {
1075
- const s = this.renderer.renderLists.get(t, 0), o = s.opaque;
1245
+ const s = this.renderer.renderLists.get(t, 0), r = s.opaque;
1076
1246
  this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix, e.matrixWorldInverse);
1077
- const r = this.targetTriangleDensity;
1078
- for (const a of o) {
1247
+ const i = this.targetTriangleDensity;
1248
+ for (const a of r) {
1079
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")) {
1080
- 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)));
1081
1251
  continue;
1082
1252
  }
1083
1253
  switch (a.material.type) {
@@ -1089,38 +1259,38 @@ class v {
1089
1259
  case "MeshDepthMaterial":
1090
1260
  continue;
1091
1261
  }
1092
- if (k === "color" && a.material && !a.object.progressive_debug_color) {
1262
+ if (R === "color" && a.material && !a.object.progressive_debug_color) {
1093
1263
  a.object.progressive_debug_color = !0;
1094
- const u = Math.random() * 16777215, y = new qe({ color: u });
1264
+ const c = Math.random() * 16777215, y = new Ve({ color: c });
1095
1265
  a.object.material = y;
1096
1266
  }
1097
- const d = a.object;
1098
- (d instanceof V || d.isMesh) && this.updateLODs(t, e, d, r);
1267
+ const u = a.object;
1268
+ (u instanceof X || u.isMesh) && this.updateLODs(t, e, u, i);
1099
1269
  }
1100
- const i = s.transparent;
1101
- for (const a of i) {
1102
- const d = a.object;
1103
- (d instanceof V || d.isMesh) && this.updateLODs(t, e, d, r);
1270
+ const o = s.transparent;
1271
+ for (const a of o) {
1272
+ const u = a.object;
1273
+ (u instanceof X || u.isMesh) && this.updateLODs(t, e, u, i);
1104
1274
  }
1105
1275
  const l = s.transmissive;
1106
1276
  for (const a of l) {
1107
- const d = a.object;
1108
- (d instanceof V || d.isMesh) && this.updateLODs(t, e, d, r);
1277
+ const u = a.object;
1278
+ (u instanceof X || u.isMesh) && this.updateLODs(t, e, u, i);
1109
1279
  }
1110
1280
  }
1111
1281
  /** Update the LOD levels for the renderer. */
1112
- updateLODs(t, e, s, o) {
1282
+ updateLODs(t, e, s, r) {
1113
1283
  s.userData || (s.userData = {});
1114
- let r = s[ce];
1115
- 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)
1116
1286
  return;
1117
1287
  for (const l of z)
1118
1288
  l.onBeforeUpdateLOD?.(this.renderer, t, e, s);
1119
- const i = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : q;
1120
- i >= 0 ? (T.mesh_lod = i, T.texture_lod = i) : (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, i), g && 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);
1121
1291
  for (const l of z)
1122
1292
  l.onAfterUpdatedLOD?.(this.renderer, t, e, s, T);
1123
- 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;
1124
1294
  }
1125
1295
  /** Load progressive textures for the given material
1126
1296
  * @param material the material to load the textures for
@@ -1130,17 +1300,17 @@ class v {
1130
1300
  loadProgressiveTextures(t, e, s) {
1131
1301
  if (!t) return;
1132
1302
  if (Array.isArray(t)) {
1133
- for (const r of t)
1134
- this.loadProgressiveTextures(r, e);
1303
+ for (const i of t)
1304
+ this.loadProgressiveTextures(i, e);
1135
1305
  return;
1136
1306
  }
1137
- let o = !1;
1138
- 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) {
1139
1309
  t[U] = e;
1140
- const r = m.assignTextureLOD(t, e).then((i) => {
1310
+ const i = m.assignTextureLOD(t, e).then((o) => {
1141
1311
  this._lodchangedlisteners.forEach((l) => l({ type: "texture", level: e, object: t }));
1142
1312
  });
1143
- de.addPromise("texture", t, r, this._newPromiseGroups);
1313
+ ce.addPromise("texture", t, i, this._newPromiseGroups);
1144
1314
  }
1145
1315
  }
1146
1316
  /** Load progressive meshes for the given mesh
@@ -1152,142 +1322,142 @@ class v {
1152
1322
  loadProgressiveMeshes(t, e) {
1153
1323
  if (!t) return Promise.resolve(null);
1154
1324
  let s = t[U] !== e;
1155
- const o = t["DEBUG:LOD"];
1156
- 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) {
1157
1327
  t[U] = e;
1158
- const r = t.geometry, i = 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));
1159
- return de.addPromise("mesh", t, i, this._newPromiseGroups), i;
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;
1160
1330
  }
1161
1331
  return Promise.resolve(null);
1162
1332
  }
1163
1333
  // private testIfLODLevelsAreAvailable() {
1164
- _sphere = new De();
1165
- _tempBox = new he();
1166
- _tempBox2 = new he();
1167
- tempMatrix = new Le();
1168
- _tempWorldPosition = new C();
1169
- _tempBoxSize = new C();
1170
- _tempBox2Size = new C();
1171
- static corner0 = new C();
1172
- static corner1 = new C();
1173
- static corner2 = new C();
1174
- static corner3 = new C();
1175
- 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();
1176
1346
  static isInside(t, e) {
1177
- const s = t.min, o = t.max, r = (s.x + o.x) * 0.5, i = (s.y + o.y) * 0.5;
1178
- return this._tempPtInside.set(r, i, 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;
1179
1349
  }
1180
1350
  static skinnedMeshBoundsFrameOffsetCounter = 0;
1181
1351
  static $skinnedMeshBoundsOffset = /* @__PURE__ */ Symbol("gltf-progressive-skinnedMeshBoundsOffset");
1182
1352
  // #region calculateLodLevel
1183
- calculateLodLevel(t, e, s, o, r) {
1353
+ calculateLodLevel(t, e, s, r, i) {
1184
1354
  if (!e) {
1185
- r.mesh_lod = -1, r.texture_lod = -1;
1355
+ i.mesh_lod = -1, i.texture_lod = -1;
1186
1356
  return;
1187
1357
  }
1188
1358
  if (!t) {
1189
- r.mesh_lod = -1, r.texture_lod = -1;
1359
+ i.mesh_lod = -1, i.texture_lod = -1;
1190
1360
  return;
1191
1361
  }
1192
1362
  let l = 10 + 1, a = !1;
1193
- if (k && e["DEBUG:LOD"] != null)
1363
+ if (R && e["DEBUG:LOD"] != null)
1194
1364
  return e["DEBUG:LOD"];
1195
- const d = m.getMeshLODExtension(e.geometry)?.lods, u = m.getPrimitiveIndex(e.geometry), y = d && d.length > 0, w = m.getMaterialMinMaxLODsCount(e.material), L = w.min_count !== 1 / 0 && w.min_count >= 0 && w.max_count >= 0;
1196
- if (!y && !L) {
1197
- r.mesh_lod = 0, r.texture_lod = 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;
1366
+ if (!y && !_) {
1367
+ i.mesh_lod = 0, i.texture_lod = 0;
1198
1368
  return;
1199
1369
  }
1200
1370
  y || (a = !0, l = 0);
1201
- const I = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
1202
- let O = e.geometry.boundingBox;
1371
+ const k = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
1372
+ let M = e.geometry.boundingBox;
1203
1373
  if (e.type === "SkinnedMesh") {
1204
- const c = e;
1205
- if (!c.boundingBox)
1206
- c.computeBoundingBox();
1374
+ const d = e;
1375
+ if (!d.boundingBox)
1376
+ d.computeBoundingBox();
1207
1377
  else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
1208
- if (!c[v.$skinnedMeshBoundsOffset]) {
1378
+ if (!d[v.$skinnedMeshBoundsOffset]) {
1209
1379
  const b = v.skinnedMeshBoundsFrameOffsetCounter++;
1210
- c[v.$skinnedMeshBoundsOffset] = b;
1380
+ d[v.$skinnedMeshBoundsOffset] = b;
1211
1381
  }
1212
- const h = c[v.$skinnedMeshBoundsOffset];
1213
- if ((s.frames + h) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
1214
- const b = Z(c), B = c.geometry;
1215
- b && (c.geometry = b), c.computeBoundingBox(), c.geometry = B;
1382
+ const g = d[v.$skinnedMeshBoundsOffset];
1383
+ if ((s.frames + g) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
1384
+ const b = ee(d), B = d.geometry;
1385
+ b && (d.geometry = b), d.computeBoundingBox(), d.geometry = B;
1216
1386
  }
1217
1387
  }
1218
- O = c.boundingBox;
1388
+ M = d.boundingBox;
1219
1389
  }
1220
- if (O) {
1221
- const c = t;
1390
+ if (M) {
1391
+ const d = t;
1222
1392
  if (e.geometry.attributes.color && e.geometry.attributes.color.count < 100 && e.geometry.boundingSphere) {
1223
1393
  this._sphere.copy(e.geometry.boundingSphere), this._sphere.applyMatrix4(e.matrixWorld);
1224
- const f = t.getWorldPosition(this._tempWorldPosition);
1225
- if (this._sphere.containsPoint(f)) {
1226
- 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;
1227
1397
  return;
1228
1398
  }
1229
1399
  }
1230
- if (this._tempBox.copy(O), this._tempBox.applyMatrix4(e.matrixWorld), c.isPerspectiveCamera && v.isInside(this._tempBox, this.projectionScreenMatrix)) {
1231
- 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;
1232
1402
  return;
1233
1403
  }
1234
- if (this._tempBox.applyMatrix4(this.projectionScreenMatrix), this.renderer.xr.enabled && c.isPerspectiveCamera && c.fov > 70) {
1235
- const f = this._tempBox.min, p = this._tempBox.max;
1236
- let D = f.x, S = f.y, G = p.x, Y = p.y;
1237
- const ee = 2, ie = 1.5, te = (f.x + p.x) * 0.5, se = (f.y + p.y) * 0.5;
1238
- D = (D - te) * ee + te, S = (S - se) * ee + se, G = (G - te) * ee + te, Y = (Y - se) * ee + se;
1239
- const Re = D < 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)), ne = Math.max(Re, $e);
1240
- s.lastCentrality = (ie - ne) * (ie - ne) * (ie - ne);
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);
1241
1411
  } else
1242
1412
  s.lastCentrality = 1;
1243
- const h = this._tempBox.getSize(this._tempBoxSize);
1244
- h.multiplyScalar(0.5), screen.availHeight > 0 && I > 0 && h.multiplyScalar(I / screen.availHeight), t.isPerspectiveCamera ? h.x *= t.aspect : t.isOrthographicCamera;
1413
+ const g = this._tempBox.getSize(this._tempBoxSize);
1414
+ g.multiplyScalar(0.5), screen.availHeight > 0 && k > 0 && g.multiplyScalar(k / screen.availHeight), t.isPerspectiveCamera ? g.x *= t.aspect : t.isOrthographicCamera;
1245
1415
  const b = t.matrixWorldInverse, B = this._tempBox2;
1246
- B.copy(O), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(b);
1247
- const M = B.getSize(this._tempBox2Size), N = Math.max(M.x, M.y);
1248
- if (Math.max(h.x, h.y) != 0 && N != 0 && (h.z = M.z / Math.max(M.x, M.y) * Math.max(h.x, h.y)), s.lastScreenCoverage = Math.max(h.x, h.y, h.z), s.lastScreenspaceVolume.copy(h), s.lastScreenCoverage *= s.lastCentrality, k && v.debugDrawLine) {
1249
- const f = this.tempMatrix.copy(this.projectionScreenMatrix);
1250
- f.invert();
1251
- const p = v.corner0, D = v.corner1, S = v.corner2, G = v.corner3;
1252
- p.copy(this._tempBox.min), D.copy(this._tempBox.max), D.x = p.x, S.copy(this._tempBox.max), S.y = p.y, G.copy(this._tempBox.max);
1416
+ B.copy(M), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(b);
1417
+ const D = B.getSize(this._tempBox2Size), N = Math.max(D.x, D.y);
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();
1421
+ const p = v.corner0, O = v.corner1, S = v.corner2, G = v.corner3;
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);
1253
1423
  const Y = (p.z + G.z) * 0.5;
1254
- p.z = D.z = S.z = G.z = Y, p.applyMatrix4(f), D.applyMatrix4(f), S.applyMatrix4(f), G.applyMatrix4(f), v.debugDrawLine(p, D, 255), v.debugDrawLine(p, S, 255), v.debugDrawLine(D, 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);
1255
1425
  }
1256
- let _ = 999;
1257
- if (d && s.lastScreenCoverage > 0)
1258
- for (let f = 0; f < d.length; f++) {
1259
- const p = d[f], S = (p.densities?.[u] || p.density || 1e-5) / s.lastScreenCoverage;
1260
- if (u > 0 && _e() && !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) {
1261
- _ = f;
1426
+ let L = 999;
1427
+ if (u && s.lastScreenCoverage > 0)
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;
1262
1432
  break;
1263
1433
  }
1264
1434
  }
1265
- _ < l && (l = _, a = !0);
1435
+ L < l && (l = L, a = !0);
1266
1436
  }
1267
- if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh, k && r.mesh_lod != s.lastLodLevel_Mesh) {
1268
- const h = d?.[r.mesh_lod];
1269
- h && console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${h.densities?.[u].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}`);
1270
1440
  }
1271
- if (L) {
1272
- const c = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
1441
+ if (_) {
1442
+ const d = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
1273
1443
  if (s.lastLodLevel_Texture < 0) {
1274
- if (r.texture_lod = w.max_count - 1, k) {
1275
- const h = w.lods[w.max_count - 1];
1276
- k && console.log(`First Texture LOD ${r.texture_lod} (${h.max_height}px) - ${e.name}`);
1444
+ if (i.texture_lod = w.max_count - 1, R) {
1445
+ const g = w.lods[w.max_count - 1];
1446
+ R && console.log(`First Texture LOD ${i.texture_lod} (${g.max_height}px) - ${e.name}`);
1277
1447
  }
1278
1448
  } else {
1279
- const h = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
1449
+ const g = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
1280
1450
  let b = s.lastScreenCoverage * 4;
1281
1451
  this.context?.engine === "model-viewer" && (b *= 1.5);
1282
- const M = I / window.devicePixelRatio * b;
1452
+ const D = k / window.devicePixelRatio * b;
1283
1453
  let N = !1;
1284
1454
  for (let P = w.lods.length - 1; P >= 0; P--) {
1285
- const _ = w.lods[P];
1286
- if (!(c && _.max_height >= 2048) && !(Ae() && _.max_height > 4096) && (_.max_height > M || !N && P === 0)) {
1287
- if (N = !0, r.texture_lod = P, k && r.texture_lod < s.lastLodLevel_Texture) {
1288
- const x = _.max_height;
1289
- console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${x}px
1290
- Screensize: ${M.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${h.toFixed(1)}
1455
+ const L = w.lods[P];
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) {
1458
+ const x = L.max_height;
1459
+ console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${i.texture_lod} = ${x}px
1460
+ Screensize: ${D.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${g.toFixed(1)}
1291
1461
  ${e.name}`);
1292
1462
  }
1293
1463
  break;
@@ -1295,61 +1465,61 @@ ${e.name}`);
1295
1465
  }
1296
1466
  }
1297
1467
  } else
1298
- r.texture_lod = 0;
1468
+ i.texture_lod = 0;
1299
1469
  }
1300
1470
  }
1301
- class pt {
1471
+ class yt {
1302
1472
  frames = 0;
1303
1473
  lastLodLevel_Mesh = -1;
1304
1474
  lastLodLevel_Texture = -1;
1305
1475
  lastScreenCoverage = 0;
1306
- lastScreenspaceVolume = new C();
1476
+ lastScreenspaceVolume = new A();
1307
1477
  lastCentrality = 0;
1308
1478
  }
1309
- const ve = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), re = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
1310
- let fe = null;
1311
- function Ie() {
1312
- const n = mt();
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();
1313
1483
  n && (n.mapURLs(function(t) {
1314
- return be(), t;
1315
- }), be(), fe?.disconnect(), fe = new MutationObserver((t) => {
1484
+ return De(), t;
1485
+ }), De(), he?.disconnect(), he = new MutationObserver((t) => {
1316
1486
  t.forEach((e) => {
1317
1487
  e.addedNodes.forEach((s) => {
1318
- s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && ke(s);
1488
+ s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ie(s);
1319
1489
  });
1320
1490
  });
1321
- }), fe.observe(document, { childList: !0, subtree: !0 }));
1491
+ }), he.observe(document, { childList: !0, subtree: !0 }));
1322
1492
  }
1323
- function mt() {
1493
+ function xt() {
1324
1494
  if (typeof customElements > "u") return null;
1325
1495
  const n = customElements.get("model-viewer");
1326
1496
  return n || (customElements.whenDefined("model-viewer").then(() => {
1327
- console.debug("[gltf-progressive] model-viewer defined"), Ie();
1497
+ console.debug("[gltf-progressive] model-viewer defined"), Re();
1328
1498
  }), null);
1329
1499
  }
1330
- function be() {
1500
+ function De() {
1331
1501
  if (typeof document > "u") return;
1332
1502
  document.querySelectorAll("model-viewer").forEach((t) => {
1333
- ke(t);
1503
+ Ie(t);
1334
1504
  });
1335
1505
  }
1336
- const Me = /* @__PURE__ */ new WeakSet();
1337
- let yt = 0;
1338
- function ke(n) {
1339
- if (!n || Me.has(n))
1506
+ const Oe = /* @__PURE__ */ new WeakSet();
1507
+ let wt = 0;
1508
+ function Ie(n) {
1509
+ if (!n || Oe.has(n))
1340
1510
  return null;
1341
- Me.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++yt + `
1511
+ Oe.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++wt + `
1342
1512
  `, n.getAttribute("src"));
1343
1513
  let t = null, e = null, s = null;
1344
- for (let o = n; o != null; o = Object.getPrototypeOf(o)) {
1345
- const r = Object.getOwnPropertySymbols(o), i = r.find((d) => d.toString() == "Symbol(renderer)"), l = r.find((d) => d.toString() == "Symbol(scene)"), a = r.find((d) => d.toString() == "Symbol(needsRender)");
1346
- !t && i != null && (t = n[i].threeRenderer), !e && l != null && (e = n[l]), !s && a != null && (s = n[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]);
1347
1517
  }
1348
1518
  if (t && e) {
1349
- let o = function() {
1519
+ let r = function() {
1350
1520
  if (s) {
1351
- let i = 0, l = setInterval(() => {
1352
- if (i++ > 5) {
1521
+ let o = 0, l = setInterval(() => {
1522
+ if (o++ > 5) {
1353
1523
  clearInterval(l);
1354
1524
  return;
1355
1525
  }
@@ -1358,23 +1528,23 @@ function ke(n) {
1358
1528
  }
1359
1529
  };
1360
1530
  console.debug("[gltf-progressive] setup model-viewer");
1361
- const r = v.get(t, { engine: "model-viewer" });
1362
- return v.addPlugin(new xt()), r.enable(), r.addEventListener("changed", () => {
1531
+ const i = v.get(t, { engine: "model-viewer" });
1532
+ return v.addPlugin(new _t()), i.enable(), i.addEventListener("changed", () => {
1363
1533
  s?.call(n);
1364
- }), n.addEventListener("model-visibility", (i) => {
1365
- i.detail.visible && s?.call(n);
1534
+ }), n.addEventListener("model-visibility", (o) => {
1535
+ o.detail.visible && s?.call(n);
1366
1536
  }), n.addEventListener("load", () => {
1367
- o();
1537
+ r();
1368
1538
  }), () => {
1369
- r.disable();
1539
+ i.disable();
1370
1540
  };
1371
1541
  }
1372
1542
  return null;
1373
1543
  }
1374
- class xt {
1544
+ class _t {
1375
1545
  _didWarnAboutMissingUrl = !1;
1376
- onBeforeUpdateLOD(t, e, s, o) {
1377
- this.tryParseMeshLOD(e, o), this.tryParseTextureLOD(e, o);
1546
+ onBeforeUpdateLOD(t, e, s, r) {
1547
+ this.tryParseMeshLOD(e, r), this.tryParseTextureLOD(e, r);
1378
1548
  }
1379
1549
  getUrl(t) {
1380
1550
  if (!t)
@@ -1389,80 +1559,80 @@ class xt {
1389
1559
  return t.element;
1390
1560
  }
1391
1561
  tryParseTextureLOD(t, e) {
1392
- if (e[re] == !0) return;
1393
- e[re] = !0;
1394
- const s = this.tryGetCurrentGLTF(t), o = this.tryGetCurrentModelViewer(t), r = this.getUrl(o);
1395
- if (r && s && e.material) {
1396
- let i = function(a) {
1397
- if (a[re] == !0) return;
1398
- a[re] = !0, a.userData && (a.userData.LOD = -1);
1399
- const d = Object.keys(a);
1400
- for (let u = 0; u < d.length; u++) {
1401
- const y = d[u], w = a[y];
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);
1569
+ const u = Object.keys(a);
1570
+ for (let c = 0; c < u.length; c++) {
1571
+ const y = u[c], w = a[y];
1402
1572
  if (w?.isTexture === !0) {
1403
- const L = w.userData?.associations?.textures;
1404
- if (L == null) continue;
1405
- const I = s.parser.json.textures[L];
1406
- if (!I) {
1407
- console.warn("Texture data not found for texture index " + L);
1573
+ const _ = w.userData?.associations?.textures;
1574
+ if (_ == null) continue;
1575
+ const k = s.parser.json.textures[_];
1576
+ if (!k) {
1577
+ console.warn("Texture data not found for texture index " + _);
1408
1578
  continue;
1409
1579
  }
1410
- if (I?.extensions?.[W]) {
1411
- const O = I.extensions[W];
1412
- O && r && m.registerTexture(r, w, O.lods.length, L, O);
1580
+ if (k?.extensions?.[W]) {
1581
+ const M = k.extensions[W];
1582
+ M && i && m.registerTexture(i, w, M.lods.length, _, M);
1413
1583
  }
1414
1584
  }
1415
1585
  }
1416
1586
  };
1417
1587
  const l = e.material;
1418
- if (Array.isArray(l)) for (const a of l) i(a);
1419
- else i(l);
1588
+ if (Array.isArray(l)) for (const a of l) o(a);
1589
+ else o(l);
1420
1590
  }
1421
1591
  }
1422
1592
  tryParseMeshLOD(t, e) {
1423
- if (e[ve] == !0) return;
1424
- e[ve] = !0;
1425
- const s = this.tryGetCurrentModelViewer(t), o = this.getUrl(s);
1426
- 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)
1427
1597
  return;
1428
- const r = e.userData?.gltfExtensions?.[W];
1429
- if (r && o) {
1430
- const i = e.uuid;
1431
- m.registerMesh(o, i, 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);
1432
1602
  }
1433
1603
  }
1434
1604
  }
1435
- function wt(...n) {
1436
- let t, e, s, o;
1605
+ function Lt(...n) {
1606
+ let t, e, s, r;
1437
1607
  switch (n.length) {
1438
1608
  case 2:
1439
- [s, e] = n, o = {};
1609
+ [s, e] = n, r = {};
1440
1610
  break;
1441
1611
  case 3:
1442
- [s, e, o] = n;
1612
+ [s, e, r] = n;
1443
1613
  break;
1444
1614
  case 4:
1445
- [t, e, s, o] = n;
1615
+ [t, e, s, r] = n;
1446
1616
  break;
1447
1617
  default:
1448
1618
  throw new Error("Invalid arguments");
1449
1619
  }
1450
- xe(e), Se(s), Te(s, {
1620
+ we(e), Te(s), Ae(s, {
1451
1621
  progressive: !0,
1452
- ...o?.hints
1453
- }), s.register((i) => new m(i));
1454
- const r = v.get(e);
1455
- 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;
1456
1626
  }
1457
- Ie();
1458
- if (!rt) {
1627
+ Re();
1628
+ if (!nt) {
1459
1629
  const n = {
1460
1630
  gltfProgressive: {
1461
- useNeedleProgressive: wt,
1631
+ useNeedleProgressive: Lt,
1462
1632
  LODsManager: v,
1463
- configureLoader: Te,
1464
- getRaycastMesh: Z,
1465
- useRaycastMeshes: it
1633
+ configureLoader: Ae,
1634
+ getRaycastMesh: ee,
1635
+ useRaycastMeshes: lt
1466
1636
  }
1467
1637
  };
1468
1638
  if (!globalThis.Needle)
@@ -1475,15 +1645,15 @@ export {
1475
1645
  W as EXTENSION_NAME,
1476
1646
  v as LODsManager,
1477
1647
  m as NEEDLE_progressive,
1478
- je as VERSION,
1479
- Se as addDracoAndKTX2Loaders,
1480
- Te as configureLoader,
1481
- xe as createLoaders,
1482
- Z as getRaycastMesh,
1483
- Ie as patchModelViewer,
1484
- ot as registerRaycastMesh,
1485
- Qe as setDracoDecoderLocation,
1486
- Je as setKTX2TranscoderLocation,
1487
- wt as useNeedleProgressive,
1488
- it 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
1489
1659
  };