@needle-tools/gltf-progressive 3.4.0-beta.1 → 3.4.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,9 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.4.0-beta.3] - 2026-03-24
8
+ - Add: `NEEDLE_progressive.maxConcurrentLoadingTasks` property to control how many LOD meshes and textures are allowed to be fetched concurrently. Defaults to `50`
9
+
7
10
  ## [3.3.5] - 2025-10-08
8
11
  - Chore: debug logging for mesh_lod use `console.log` instead of `console.debug`
9
12
 
package/README.md CHANGED
@@ -155,6 +155,15 @@ These settings are available on the LOD manager instance:
155
155
  - `manual` - When set to true the LODsManager will not update the LODs. This can be used to manually update the LODs using the `update` method. Otherwise the LODs will be updated automatically when the renderer renders the scene.
156
156
  - `overrideLodLevel` - Can be set to any number between 0 and 6 to override the lod level to be loaded. To disable the override again set it to `undefined`.
157
157
 
158
+ ### Concurrent loading limit
159
+ `maxConcurrentLoadingTasks` controls how many LOD resources (meshes and textures) can be loaded simultaneously. When the limit is reached, additional requests are queued and processed as previous ones finish. Default: `50`.
160
+
161
+ ```ts
162
+ import { NEEDLE_progressive } from "@needle-tools/gltf-progressive";
163
+
164
+ NEEDLE_progressive.maxConcurrentLoadingTasks = 20;
165
+ ```
166
+
158
167
  ### Automatically use low-poly meshes for raycasting
159
168
  Simply call `useRaycastMeshes(true)` to enable faster raycasting when using the the THREE.Raycaster. This can again be disabled by calling `useRaycastMeshes(false)`. Calling this method is only necessary once to enable it.
160
169
 
@@ -1,15 +1,15 @@
1
- import { BufferGeometry as V, Mesh as X, Box3 as ge, Vector3 as A, Sphere as Oe, CompressedTexture as Ge, Texture as F, Matrix3 as Fe, InterleavedBuffer as We, InterleavedBufferAttribute as Ue, BufferAttribute as ze, TextureLoader as Ne, Matrix4 as Le, Clock as qe, MeshStandardMaterial as Ee } from "three";
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 Ne, Matrix4 as ve, Clock as qe, MeshStandardMaterial as Ve } from "three";
2
2
  import { GLTFLoader as xe } from "three/examples/jsm/loaders/GLTFLoader.js";
3
- import { MeshoptDecoder as Ve } from "three/examples/jsm/libs/meshopt_decoder.module.js";
3
+ import { MeshoptDecoder as Ee } from "three/examples/jsm/libs/meshopt_decoder.module.js";
4
4
  import { DRACOLoader as Xe } from "three/examples/jsm/loaders/DRACOLoader.js";
5
5
  import { KTX2Loader as je } from "three/examples/jsm/loaders/KTX2Loader.js";
6
6
  const Ke = "";
7
7
  globalThis.GLTF_PROGRESSIVE_VERSION = Ke;
8
8
  console.debug("[gltf-progressive] version -");
9
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, Se = new URL(I + "draco_decoder.js");
11
- Se.searchParams.append("range", "true");
12
- fetch(Se, {
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"
@@ -17,7 +17,7 @@ fetch(Se, {
17
17
  }).catch((n) => {
18
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
- Te();
20
+ Ce();
21
21
  });
22
22
  const Qe = () => ({
23
23
  dracoDecoderPath: I,
@@ -30,18 +30,18 @@ function Ze(n) {
30
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
32
  function we(n) {
33
- return Te(), n ? $.detectSupport(n) : n !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: C, ktx2Loader: $, meshoptDecoder: oe };
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 Pe(n) {
35
+ function Te(n) {
36
36
  n.dracoLoader || n.setDRACOLoader(C), n.ktx2Loader || n.setKTX2Loader($), n.meshoptDecoder || n.setMeshoptDecoder(oe);
37
37
  }
38
38
  const pe = /* @__PURE__ */ Symbol("dracoDecoderPath");
39
39
  let C, oe, $;
40
- function Te() {
41
- C || (C = new Xe(), C[pe] = I, C.setDecoderPath(I), C.setDecoderConfig({ type: "js" }), C.preload()), $ || ($ = new je(), $.setTranscoderPath(j), $.init()), oe || (oe = Ve);
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
43
  const me = /* @__PURE__ */ new WeakMap();
44
- function Ce(n, t) {
44
+ function Ae(n, t) {
45
45
  let e = me.get(n);
46
46
  e ? e = Object.assign(e, t) : e = t, me.set(n, e);
47
47
  }
@@ -74,17 +74,17 @@ function st(n, t) {
74
74
  }
75
75
  return t;
76
76
  }
77
- function Ae() {
77
+ function _e() {
78
78
  return H !== void 0 || (H = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), N("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", H)), H;
79
79
  }
80
80
  let H;
81
- function ve() {
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
86
  class rt {
87
- constructor(t = 100, e = {}) {
87
+ constructor(t, e = {}) {
88
88
  this.maxConcurrent = t, this.debug = e.debug ?? !1, window.requestAnimationFrame(this.tick);
89
89
  }
90
90
  _running = /* @__PURE__ */ new Map();
@@ -131,7 +131,7 @@ function ot(n) {
131
131
  }
132
132
  const nt = typeof window > "u" && typeof document > "u", ye = /* @__PURE__ */ Symbol("needle:raycast-mesh");
133
133
  function ee(n) {
134
- return n?.[ye] instanceof V ? n[ye] : null;
134
+ return n?.[ye] instanceof E ? n[ye] : null;
135
135
  }
136
136
  function at(n, t) {
137
137
  if ((n.type === "Mesh" || n.type === "SkinnedMesh") && !ee(n)) {
@@ -155,21 +155,21 @@ function lt(n = !0) {
155
155
  }
156
156
  let Q = null;
157
157
  function ut(n) {
158
- const t = new V();
158
+ const t = new E();
159
159
  for (const e in n.attributes)
160
160
  t.setAttribute(e, n.getAttribute(e));
161
161
  return t.setIndex(n.getIndex()), t;
162
162
  }
163
163
  const z = new Array(), f = N("debugprogressive");
164
- let Z, E = -1;
164
+ let Z, V = -1;
165
165
  if (f) {
166
166
  let n = function() {
167
- E += 1, E >= t && (E = -1), console.log(`Toggle LOD level [${E}]`);
167
+ V += 1, V >= t && (V = -1), console.log(`Toggle LOD level [${V}]`);
168
168
  }, t = 6;
169
169
  window.addEventListener("keyup", (e) => {
170
170
  e.key === "p" && n(), e.key === "w" && (Z = !Z, console.log(`Toggle wireframe [${Z}]`));
171
171
  const s = parseInt(e.key);
172
- !isNaN(s) && s >= 0 && (E = s, console.log(`Set LOD level to [${E}]`));
172
+ !isNaN(s) && s >= 0 && (V = s, console.log(`Set LOD level to [${V}]`));
173
173
  });
174
174
  }
175
175
  function ke(n) {
@@ -181,18 +181,18 @@ function ke(n) {
181
181
  }
182
182
  const J = new Array();
183
183
  let ct = 0;
184
- const dt = Ae() ? 2 : 10;
184
+ const dt = _e() ? 2 : 10;
185
185
  function ft(n) {
186
186
  if (J.length < dt) {
187
187
  const s = J.length;
188
188
  f && console.warn(`[Worker] Creating new worker #${s}`);
189
- const r = _e.createWorker(n || {});
189
+ const r = Le.createWorker(n || {});
190
190
  return J.push(r), r;
191
191
  }
192
192
  const t = ct++ % J.length;
193
193
  return J[t];
194
194
  }
195
- class _e {
195
+ class Le {
196
196
  constructor(t, e) {
197
197
  this.worker = t, this._debug = e.debug ?? !1, t.onmessage = (s) => {
198
198
  const r = s.data;
@@ -214,12 +214,12 @@ class _e {
214
214
  static async createWorker(t) {
215
215
  const e = new Worker(new URL(
216
216
  /* @vite-ignore */
217
- "/assets/loader.worker-CCrD-Ycm.js",
217
+ "/assets/gltf-progressive.worker-CCrD-Ycm.js",
218
218
  import.meta.url
219
219
  ), {
220
220
  type: "module"
221
221
  });
222
- return new _e(e, t);
222
+ return new Le(e, t);
223
223
  }
224
224
  _running = [];
225
225
  _webglRenderer = null;
@@ -250,7 +250,7 @@ class _e {
250
250
  }
251
251
  function ht(n) {
252
252
  for (const t of n.geometries) {
253
- const e = t.geometry, s = new V();
253
+ const e = t.geometry, s = new E();
254
254
  if (s.name = e.name || "", e.index) {
255
255
  const r = e.index;
256
256
  s.setIndex(le(r));
@@ -272,7 +272,7 @@ function ht(n) {
272
272
  e.boundingBox?.max.x,
273
273
  e.boundingBox?.max.y,
274
274
  e.boundingBox?.max.z
275
- ), s.boundingSphere = new Oe(
275
+ ), s.boundingSphere = new Se(
276
276
  new A(
277
277
  e.boundingSphere?.center.x,
278
278
  e.boundingSphere?.center.y,
@@ -335,7 +335,7 @@ function le(n) {
335
335
  const gt = N("gltf-progressive-worker");
336
336
  N("gltf-progressive-reduce-mipmaps");
337
337
  const K = N("gltf-progressive-gc"), ue = /* @__PURE__ */ Symbol("needle-progressive-texture"), W = "NEEDLE_progressive";
338
- class y {
338
+ class m {
339
339
  /** The name of the extension */
340
340
  get name() {
341
341
  return W;
@@ -381,9 +381,9 @@ class y {
381
381
  const c = s.lodInfos.get(u.key);
382
382
  if (c && c.lods) {
383
383
  a.min_count = Math.min(a.min_count, c.lods.length), a.max_count = Math.max(a.max_count, c.lods.length);
384
- for (let m = 0; m < c.lods.length; m++) {
385
- const w = c.lods[m];
386
- w.width && (a.lods[m] = a.lods[m] || { min_height: 1 / 0, max_height: 0 }, a.lods[m].min_height = Math.min(a.lods[m].min_height, w.height), a.lods[m].max_height = Math.max(a.lods[m].max_height, w.height));
384
+ for (let y = 0; y < c.lods.length; y++) {
385
+ const w = c.lods[y];
386
+ 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));
387
387
  }
388
388
  }
389
389
  }
@@ -443,7 +443,7 @@ class y {
443
443
  return Promise.resolve(null);
444
444
  for (const i of z)
445
445
  i.onBeforeGetLODMesh?.(t, e);
446
- return t["LOD:requested level"] = e, y.getOrLoadLOD(s, e).then((i) => {
446
+ return t["LOD:requested level"] = e, m.getOrLoadLOD(s, e).then((i) => {
447
447
  if (Array.isArray(i)) {
448
448
  const o = r.index || 0;
449
449
  i = i[o];
@@ -506,9 +506,19 @@ class y {
506
506
  }
507
507
  return Promise.resolve(null);
508
508
  }
509
+ /**
510
+ * Set the maximum number of concurrent loading tasks for LOD resources. This limits how many LOD resources (meshes or textures) can be loaded at the same time to prevent overloading the network or GPU. If the limit is reached, additional loading requests will be queued and processed as previous ones finish.
511
+ * @default 50 on desktop, 20 on mobile devices
512
+ */
513
+ static set maxConcurrentLoadingTasks(t) {
514
+ m.queue.maxConcurrent = t;
515
+ }
516
+ static get maxConcurrentLoadingTasks() {
517
+ return m.queue.maxConcurrent;
518
+ }
509
519
  // #region INTERNAL
510
520
  static assignTextureLODForSlot(t, e, s, r) {
511
- return t?.isTexture !== !0 ? Promise.resolve(null) : r === "glyphMap" ? Promise.resolve(t) : y.getOrLoadLOD(t, e).then((i) => {
521
+ return t?.isTexture !== !0 ? Promise.resolve(null) : r === "glyphMap" ? Promise.resolve(t) : m.getOrLoadLOD(t, e).then((i) => {
512
522
  if (Array.isArray(i))
513
523
  return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."), null;
514
524
  if (i?.isTexture === !0) {
@@ -540,7 +550,7 @@ class y {
540
550
  loadMesh = (t) => {
541
551
  if (this._isLoadingMesh) return null;
542
552
  const e = this.parser.json.meshes[t]?.extensions?.[W];
543
- return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && y.registerMesh(this.url, e.guid, s, e.lods?.length, 0, e), s))) : null;
553
+ return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && m.registerMesh(this.url, e.guid, s, e.lods?.length, 0, e), s))) : null;
544
554
  };
545
555
  // private _isLoadingTexture;
546
556
  // loadTexture = (textureIndex: number) => {
@@ -567,9 +577,9 @@ class y {
567
577
  }
568
578
  let i = !1;
569
579
  for (const o of this.parser.associations.keys())
570
- o.isTexture === !0 && this.parser.associations.get(o)?.textures === s && (i = !0, y.registerTexture(this.url, o, r.lods?.length, s, r));
580
+ o.isTexture === !0 && this.parser.associations.get(o)?.textures === s && (i = !0, m.registerTexture(this.url, o, r.lods?.length, s, r));
571
581
  i || this.parser.getDependency("texture", s).then((o) => {
572
- o && y.registerTexture(this.url, o, r.lods?.length, s, r);
582
+ o && m.registerTexture(this.url, o, r.lods?.length, s, r);
573
583
  });
574
584
  }
575
585
  }
@@ -580,7 +590,7 @@ class y {
580
590
  for (const i of this.parser.associations.keys())
581
591
  if (i.isMesh) {
582
592
  const o = this.parser.associations.get(i);
583
- o?.meshes === s && y.registerMesh(this.url, r.guid, i, r.lods.length, o.primitives, r);
593
+ o?.meshes === s && m.registerMesh(this.url, r.guid, i, r.lods.length, o.primitives, r);
584
594
  }
585
595
  }
586
596
  }
@@ -600,7 +610,7 @@ class y {
600
610
  }
601
611
  e.source && (e.source[ue] = i);
602
612
  const o = i.guid;
603
- y.assignLODInformation(t, e, o, s, r), y.lodInfos.set(o, i), y.lowresCache.set(o, new WeakRef(e));
613
+ m.assignLODInformation(t, e, o, s, r), m.lodInfos.set(o, i), m.lowresCache.set(o, new WeakRef(e));
604
614
  };
605
615
  /**
606
616
  * Register a mesh with LOD information
@@ -611,9 +621,9 @@ class y {
611
621
  f && console.warn("gltf-progressive: Register mesh without geometry");
612
622
  return;
613
623
  }
614
- l.userData || (l.userData = {}), f && console.log("> Progressive: register mesh " + s.name, { index: i, uuid: s.uuid }, o, s), y.assignLODInformation(t, l, e, r, i), y.lodInfos.set(e, o);
615
- let u = y.lowresCache.get(e)?.deref();
616
- u ? u.push(s.geometry) : u = [s.geometry], y.lowresCache.set(e, new WeakRef(u)), r > 0 && !ee(s) && at(s, l);
624
+ 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);
625
+ let u = m.lowresCache.get(e)?.deref();
626
+ u ? u.push(s.geometry) : u = [s.geometry], m.lowresCache.set(e, new WeakRef(u)), r > 0 && !ee(s) && at(s, l);
617
627
  for (const c of z)
618
628
  c.onRegisteredNewMesh?.(s, o);
619
629
  };
@@ -688,9 +698,9 @@ class y {
688
698
  * The held value is the cache key string used in `previouslyLoaded`.
689
699
  */
690
700
  static _resourceRegistry = new FinalizationRegistry((t) => {
691
- const e = y.cache.get(t);
701
+ const e = m.cache.get(t);
692
702
  (f || K) && console.debug(`[gltf-progressive] Memory: Resource GC'd
693
- ${t}`), e instanceof WeakRef && (e.deref() || (y.cache.delete(t), (f || K) && console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")));
703
+ ${t}`), e instanceof WeakRef && (e.deref() || (m.cache.delete(t), (f || K) && console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")));
694
704
  });
695
705
  /**
696
706
  * Track texture usage by incrementing reference count
@@ -715,8 +725,8 @@ ${t}`), e instanceof WeakRef && (e.deref() || (y.cache.delete(t), (f || K) && co
715
725
  function i(o, l) {
716
726
  let a = t.image?.width || t.source?.data?.width || 0, u = t.image?.height || t.source?.data?.height || 0;
717
727
  const c = a && u ? `${a}x${u}` : "N/A";
718
- let m = "N/A";
719
- a && u && (m = `~${(it(t) / (1024 * 1024)).toFixed(2)} MB`), console.log(`${o} — ${t.name} ${c} (${m}), refCount: ${s} → ${l}
728
+ let y = "N/A";
729
+ a && u && (y = `~${(it(t) / (1024 * 1024)).toFixed(2)} MB`), console.log(`${o} — ${t.name} ${c} (${y}), refCount: ${s} → ${l}
720
730
  ${e}`);
721
731
  }
722
732
  }
@@ -732,13 +742,13 @@ ${e}`);
732
742
  const a = t;
733
743
  a.source && a.source[ue] && (o = a.source[ue]);
734
744
  }
735
- if (o || (o = y.lodInfos.get(i)), !o)
736
- f && console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`, t.type, y.lodInfos);
745
+ if (o || (o = m.lodInfos.get(i)), !o)
746
+ f && console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`, t.type, m.lodInfos);
737
747
  else {
738
748
  if (e > 0) {
739
749
  let c = !1;
740
- const m = Array.isArray(o.lods);
741
- if (m && e >= o.lods.length ? c = !0 : m || (c = !0), c) {
750
+ const y = Array.isArray(o.lods);
751
+ if (y && e >= o.lods.length ? c = !0 : y || (c = !0), c) {
742
752
  const w = this.lowresCache.get(i);
743
753
  if (w) {
744
754
  const _ = w.deref();
@@ -755,23 +765,23 @@ ${e}`);
755
765
  if (u.endsWith(".glb") || u.endsWith(".gltf")) {
756
766
  if (!o.guid)
757
767
  return console.warn("missing pointer for glb/gltf texture", o), null;
758
- const c = u + "_" + o.guid, m = await this.queue.slot(u), w = this.cache.get(c);
768
+ const c = u + "_" + o.guid, y = await this.queue.slot(u), w = this.cache.get(c);
759
769
  if (w !== void 0)
760
770
  if (s && console.log(`LOD ${e} was already loading/loaded: ${c}`), w instanceof WeakRef) {
761
771
  const d = w.deref();
762
772
  if (d) {
763
773
  let g = d, b = !1;
764
- if (g instanceof F && t instanceof F ? g.image?.data || g.source?.data ? g = this.copySettings(t, g) : b = !0 : g instanceof V && t instanceof V && (g.attributes.position?.array || (b = !0)), !b)
774
+ 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)
765
775
  return g;
766
776
  }
767
777
  this.cache.delete(c), f && console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${c}`);
768
778
  } else {
769
779
  let d = await w.catch((b) => (console.error(`Error loading LOD ${e} from ${u}
770
780
  `, b), null)), g = !1;
771
- 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 V && t instanceof V && (d.attributes.position?.array || (g = !0, this.cache.delete(c)))), !g)
781
+ 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)
772
782
  return d;
773
783
  }
774
- if (!m.use)
784
+ if (!y.use)
775
785
  return f && console.log(`LOD ${e} was aborted: ${u}`), null;
776
786
  const _ = o, k = new Promise(async (d, g) => {
777
787
  if (gt) {
@@ -779,20 +789,20 @@ ${e}`);
779
789
  if (x.textures.length > 0)
780
790
  for (const h of x.textures) {
781
791
  let p = h.texture;
782
- return y.assignLODInformation(r.url, p, i, e, void 0), t instanceof F && (p = this.copySettings(t, p)), p && (p.guid = _.guid), d(p);
792
+ return m.assignLODInformation(r.url, p, i, e, void 0), t instanceof F && (p = this.copySettings(t, p)), p && (p.guid = _.guid), d(p);
783
793
  }
784
794
  if (x.geometries.length > 0) {
785
795
  const h = new Array();
786
796
  for (const p of x.geometries) {
787
797
  const O = p.geometry;
788
- y.assignLODInformation(r.url, O, i, e, p.primitiveIndex), h.push(O);
798
+ m.assignLODInformation(r.url, O, i, e, p.primitiveIndex), h.push(O);
789
799
  }
790
800
  return d(h);
791
801
  }
792
802
  return d(null);
793
803
  }
794
804
  const b = new xe();
795
- Pe(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
805
+ Te(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
796
806
  let B = u;
797
807
  if (_ && Array.isArray(_.lods)) {
798
808
  const L = _.lods[e];
@@ -819,7 +829,7 @@ ${e}`);
819
829
  }
820
830
  if (L) {
821
831
  let x = await q.getDependency("texture", P);
822
- return x && y.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);
832
+ 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);
823
833
  } else f && console.warn("Could not find texture with guid", _.guid, D.parser.json);
824
834
  }
825
835
  if (P = 0, D.parser.json.meshes) {
@@ -838,14 +848,14 @@ ${e}`);
838
848
  const x = await q.getDependency("mesh", P);
839
849
  if (s && console.log(`Loaded Mesh "${x.name}"`, u, P, x, c), x.isMesh === !0) {
840
850
  const h = x.geometry;
841
- return y.assignLODInformation(r.url, h, i, e, 0), d(h);
851
+ return m.assignLODInformation(r.url, h, i, e, 0), d(h);
842
852
  } else {
843
853
  const h = new Array();
844
854
  for (let p = 0; p < x.children.length; p++) {
845
855
  const O = x.children[p];
846
856
  if (O.isMesh === !0) {
847
857
  const S = O.geometry;
848
- y.assignLODInformation(r.url, S, i, e, p), h.push(S);
858
+ m.assignLODInformation(r.url, S, i, e, p), h.push(S);
849
859
  }
850
860
  }
851
861
  return d(h);
@@ -854,19 +864,18 @@ ${e}`);
854
864
  }
855
865
  return d(null);
856
866
  });
857
- this.cache.set(c, k), m.use(k);
867
+ this.cache.set(c, k), y.use(k);
858
868
  const M = await k;
859
- return M != null ? M instanceof F ? (this.cache.set(c, new WeakRef(M)), y._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;
869
+ 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;
860
870
  } else if (t instanceof F) {
861
871
  s && console.log("Load texture from uri: " + u);
862
- const m = await new Ne().loadAsync(u);
863
- return m ? (m.guid = o.guid, m.flipY = !1, m.needsUpdate = !0, m.colorSpace = t.colorSpace, s && console.log(o, m)) : f && console.warn("failed loading", u), m;
872
+ const y = await new Ne().loadAsync(u);
873
+ 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;
864
874
  }
865
875
  }
866
876
  return null;
867
877
  }
868
- static maxConcurrent = 50;
869
- static queue = new rt(y.maxConcurrent, { debug: f != !1 });
878
+ static queue = new rt(_e() ? 20 : 50, { debug: f != !1 });
870
879
  static assignLODInformation(t, e, s, r, i) {
871
880
  if (!e) return;
872
881
  e.userData || (e.userData = {});
@@ -1003,7 +1012,7 @@ class v {
1003
1012
  }
1004
1013
  renderer;
1005
1014
  context;
1006
- projectionScreenMatrix = new Le();
1015
+ projectionScreenMatrix = new ve();
1007
1016
  /** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
1008
1017
  get plugins() {
1009
1018
  return z;
@@ -1052,7 +1061,7 @@ class v {
1052
1061
  const r = performance.now();
1053
1062
  return s.ready.finally(() => {
1054
1063
  const i = this._newPromiseGroups.indexOf(s);
1055
- i >= 0 && (this._newPromiseGroups.splice(i, 1), ve() && performance.measure("LODsManager:awaitLoading", {
1064
+ i >= 0 && (this._newPromiseGroups.splice(i, 1), be() && performance.measure("LODsManager:awaitLoading", {
1056
1065
  start: r,
1057
1066
  detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
1058
1067
  }));
@@ -1143,8 +1152,8 @@ class v {
1143
1152
  }
1144
1153
  if (R === "color" && a.material && !a.object.progressive_debug_color) {
1145
1154
  a.object.progressive_debug_color = !0;
1146
- const c = Math.random() * 16777215, m = new Ee({ color: c });
1147
- a.object.material = m;
1155
+ const c = Math.random() * 16777215, y = new Ve({ color: c });
1156
+ a.object.material = y;
1148
1157
  }
1149
1158
  const u = a.object;
1150
1159
  (u instanceof X || u.isMesh) && this.updateLODs(t, e, u, i);
@@ -1168,7 +1177,7 @@ class v {
1168
1177
  return;
1169
1178
  for (const l of z)
1170
1179
  l.onBeforeUpdateLOD?.(this.renderer, t, e, s);
1171
- const o = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : E;
1180
+ const o = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : V;
1172
1181
  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);
1173
1182
  for (const l of z)
1174
1183
  l.onAfterUpdatedLOD?.(this.renderer, t, e, s, T);
@@ -1189,7 +1198,7 @@ class v {
1189
1198
  let r = !1;
1190
1199
  if ((t[U] === void 0 || e < t[U]) && (r = !0), s !== void 0 && s >= 0 && (r = t[U] != s, e = s), r) {
1191
1200
  t[U] = e;
1192
- const i = y.assignTextureLOD(t, e).then((o) => {
1201
+ const i = m.assignTextureLOD(t, e).then((o) => {
1193
1202
  this._lodchangedlisteners.forEach((l) => l({ type: "texture", level: e, object: t }));
1194
1203
  });
1195
1204
  ce.addPromise("texture", t, i, this._newPromiseGroups);
@@ -1207,16 +1216,16 @@ class v {
1207
1216
  const r = t["DEBUG:LOD"];
1208
1217
  if (r != null && (s = t[U] != r, e = r), s) {
1209
1218
  t[U] = e;
1210
- const i = t.geometry, o = y.assignMeshLOD(t, e).then((l) => (l && t[U] == e && i != t.geometry && this._lodchangedlisteners.forEach((a) => a({ type: "mesh", level: e, object: t })), l));
1219
+ 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));
1211
1220
  return ce.addPromise("mesh", t, o, this._newPromiseGroups), o;
1212
1221
  }
1213
1222
  return Promise.resolve(null);
1214
1223
  }
1215
1224
  // private testIfLODLevelsAreAvailable() {
1216
- _sphere = new Oe();
1225
+ _sphere = new Se();
1217
1226
  _tempBox = new ge();
1218
1227
  _tempBox2 = new ge();
1219
- tempMatrix = new Le();
1228
+ tempMatrix = new ve();
1220
1229
  _tempWorldPosition = new A();
1221
1230
  _tempBoxSize = new A();
1222
1231
  _tempBox2Size = new A();
@@ -1244,12 +1253,12 @@ class v {
1244
1253
  let l = 10 + 1, a = !1;
1245
1254
  if (R && e["DEBUG:LOD"] != null)
1246
1255
  return e["DEBUG:LOD"];
1247
- const u = y.getMeshLODExtension(e.geometry)?.lods, c = y.getPrimitiveIndex(e.geometry), m = u && u.length > 0, w = y.getMaterialMinMaxLODsCount(e.material), _ = w.min_count !== 1 / 0 && w.min_count >= 0 && w.max_count >= 0;
1248
- if (!m && !_) {
1256
+ 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;
1257
+ if (!y && !_) {
1249
1258
  i.mesh_lod = 0, i.texture_lod = 0;
1250
1259
  return;
1251
1260
  }
1252
- m || (a = !0, l = 0);
1261
+ y || (a = !0, l = 0);
1253
1262
  const k = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
1254
1263
  let M = e.geometry.boundingBox;
1255
1264
  if (e.type === "SkinnedMesh") {
@@ -1309,7 +1318,7 @@ class v {
1309
1318
  if (u && s.lastScreenCoverage > 0)
1310
1319
  for (let h = 0; h < u.length; h++) {
1311
1320
  const p = u[h], S = (p.densities?.[c] || p.density || 1e-5) / s.lastScreenCoverage;
1312
- if (c > 0 && ve() && !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) {
1321
+ 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) {
1313
1322
  L = h;
1314
1323
  break;
1315
1324
  }
@@ -1335,7 +1344,7 @@ class v {
1335
1344
  let q = !1;
1336
1345
  for (let P = w.lods.length - 1; P >= 0; P--) {
1337
1346
  const L = w.lods[P];
1338
- if (!(d && L.max_height >= 2048) && !(Ae() && L.max_height > 4096) && (L.max_height > D || !q && P === 0)) {
1347
+ if (!(d && L.max_height >= 2048) && !(_e() && L.max_height > 4096) && (L.max_height > D || !q && P === 0)) {
1339
1348
  if (q = !0, i.texture_lod = P, R && i.texture_lod < s.lastLodLevel_Texture) {
1340
1349
  const x = L.max_height;
1341
1350
  console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${i.texture_lod} = ${x}px
@@ -1358,13 +1367,13 @@ class yt {
1358
1367
  lastScreenspaceVolume = new A();
1359
1368
  lastCentrality = 0;
1360
1369
  }
1361
- const be = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ie = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
1370
+ const Me = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ie = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
1362
1371
  let he = null;
1363
1372
  function Re() {
1364
1373
  const n = xt();
1365
1374
  n && (n.mapURLs(function(t) {
1366
- return Me(), t;
1367
- }), Me(), he?.disconnect(), he = new MutationObserver((t) => {
1375
+ return De(), t;
1376
+ }), De(), he?.disconnect(), he = new MutationObserver((t) => {
1368
1377
  t.forEach((e) => {
1369
1378
  e.addedNodes.forEach((s) => {
1370
1379
  s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ie(s);
@@ -1379,18 +1388,18 @@ function xt() {
1379
1388
  console.debug("[gltf-progressive] model-viewer defined"), Re();
1380
1389
  }), null);
1381
1390
  }
1382
- function Me() {
1391
+ function De() {
1383
1392
  if (typeof document > "u") return;
1384
1393
  document.querySelectorAll("model-viewer").forEach((t) => {
1385
1394
  Ie(t);
1386
1395
  });
1387
1396
  }
1388
- const De = /* @__PURE__ */ new WeakSet();
1397
+ const Oe = /* @__PURE__ */ new WeakSet();
1389
1398
  let wt = 0;
1390
1399
  function Ie(n) {
1391
- if (!n || De.has(n))
1400
+ if (!n || Oe.has(n))
1392
1401
  return null;
1393
- De.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++wt + `
1402
+ Oe.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++wt + `
1394
1403
  `, n.getAttribute("src"));
1395
1404
  let t = null, e = null, s = null;
1396
1405
  for (let r = n; r != null; r = Object.getPrototypeOf(r)) {
@@ -1450,7 +1459,7 @@ class _t {
1450
1459
  a[ie] = !0, a.userData && (a.userData.LOD = -1);
1451
1460
  const u = Object.keys(a);
1452
1461
  for (let c = 0; c < u.length; c++) {
1453
- const m = u[c], w = a[m];
1462
+ const y = u[c], w = a[y];
1454
1463
  if (w?.isTexture === !0) {
1455
1464
  const _ = w.userData?.associations?.textures;
1456
1465
  if (_ == null) continue;
@@ -1461,7 +1470,7 @@ class _t {
1461
1470
  }
1462
1471
  if (k?.extensions?.[W]) {
1463
1472
  const M = k.extensions[W];
1464
- M && i && y.registerTexture(i, w, M.lods.length, _, M);
1473
+ M && i && m.registerTexture(i, w, M.lods.length, _, M);
1465
1474
  }
1466
1475
  }
1467
1476
  }
@@ -1472,15 +1481,15 @@ class _t {
1472
1481
  }
1473
1482
  }
1474
1483
  tryParseMeshLOD(t, e) {
1475
- if (e[be] == !0) return;
1476
- e[be] = !0;
1484
+ if (e[Me] == !0) return;
1485
+ e[Me] = !0;
1477
1486
  const s = this.tryGetCurrentModelViewer(t), r = this.getUrl(s);
1478
1487
  if (!r)
1479
1488
  return;
1480
1489
  const i = e.userData?.gltfExtensions?.[W];
1481
1490
  if (i && r) {
1482
1491
  const o = e.uuid;
1483
- y.registerMesh(r, o, e, 0, i.lods.length, i);
1492
+ m.registerMesh(r, o, e, 0, i.lods.length, i);
1484
1493
  }
1485
1494
  }
1486
1495
  }
@@ -1499,10 +1508,10 @@ function Lt(...n) {
1499
1508
  default:
1500
1509
  throw new Error("Invalid arguments");
1501
1510
  }
1502
- we(e), Pe(s), Ce(s, {
1511
+ we(e), Te(s), Ae(s, {
1503
1512
  progressive: !0,
1504
1513
  ...r?.hints
1505
- }), s.register((o) => new y(o));
1514
+ }), s.register((o) => new m(o));
1506
1515
  const i = v.get(e);
1507
1516
  return r?.enableLODsManager !== !1 && i.enable(), i;
1508
1517
  }
@@ -1512,7 +1521,7 @@ if (!nt) {
1512
1521
  gltfProgressive: {
1513
1522
  useNeedleProgressive: Lt,
1514
1523
  LODsManager: v,
1515
- configureLoader: Ce,
1524
+ configureLoader: Ae,
1516
1525
  getRaycastMesh: ee,
1517
1526
  useRaycastMeshes: lt
1518
1527
  }
@@ -1526,10 +1535,10 @@ if (!nt) {
1526
1535
  export {
1527
1536
  W as EXTENSION_NAME,
1528
1537
  v as LODsManager,
1529
- y as NEEDLE_progressive,
1538
+ m as NEEDLE_progressive,
1530
1539
  Ke as VERSION,
1531
- Pe as addDracoAndKTX2Loaders,
1532
- Ce as configureLoader,
1540
+ Te as addDracoAndKTX2Loaders,
1541
+ Ae as configureLoader,
1533
1542
  we as createLoaders,
1534
1543
  ee as getRaycastMesh,
1535
1544
  Re as patchModelViewer,
@@ -1,10 +1,10 @@
1
- import{BufferGeometry as U,Mesh as F,Box3 as ie,Vector3 as A,Sphere as Le,CompressedTexture as je,Texture as $,Matrix3 as We,InterleavedBuffer as Ue,InterleavedBufferAttribute as Fe,BufferAttribute as ze,TextureLoader as qe,Matrix4 as _e,Clock as Ve,MeshStandardMaterial as Xe}from"three";import{GLTFLoader as ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ke}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ye}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const be="";globalThis.GLTF_PROGRESSIVE_VERSION=be,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Qe=k,Je=z,Me=new URL(k+"draco_decoder.js");Me.searchParams.append("range","true"),fetch(Me,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Qe&&De("./include/draco/"),z===Je&&Oe("./include/ktx2/")}).finally(()=>{Se()});const Ze=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function De(i){k=i,T&&T[ue]!=k?(console.debug("Updating Draco decoder path to "+i),T[ue]=k,T.setDecoderPath(k),T.preload()):console.debug("Setting Draco decoder path to "+i)}function Oe(i){z=i,I&&I.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),I.setTranscoderPath(z),I.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Se(),i?I.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:I,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(I),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let T,te,I;function Se(){T||(T=new Ye,T[ue]=k,T.setDecoderPath(k),T.setDecoderConfig({type:"js"}),T.preload()),I||(I=new He,I.setTranscoderPath(z),I.init()),te||(te=Ke)}const ce=new WeakMap;function de(i,t){let e=ce.get(i);e?e=Object.assign(e,t):e=t,ce.set(i,e)}const et=ae.prototype.load;function tt(...i){const t=ce.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return i[0]=e,et?.call(this,...i)}ae.prototype.load=tt,N("debugprogressive");function N(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function st(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Te(){return se!==void 0||(se=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",se)),se}let se;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class rt{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}function ot(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,o=Math.floor(Math.log2(Math.max(t,e,s)))+1,r=nt(i);return t*e*s*r*(1-Math.pow(.25,o))/(1-.25)}function nt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const o=i.type;return o===1009||o===1010?s=1:o===1011||o===1012?s=2:o===1013||o===1014||o===1015?s=4:o===1016&&(s=2),t*s}const it=typeof window>"u"&&typeof document>"u",he=Symbol("needle:raycast-mesh");function V(i){return i?.[he]instanceof U?i[he]:null}function Ce(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=at(t);e.userData={isRaycastMesh:!0},i[he]=e}}function Ae(i=!0){if(i){if(K)return;const t=K=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!K)return;F.prototype.raycast=K,K=null}}let K=null;function at(i){const t=new U;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const j=new Array,h=N("debugprogressive");let Y,q=-1;if(h){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Y=!Y,console.log(`Toggle wireframe [${Y}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function ke(i){if(h&&Y!==void 0)if(Array.isArray(i))for(const t of i)ke(t);else i&&"wireframe"in i&&(i.wireframe=Y===!0)}const H=new Array;let lt=0;const ut=Te()?2:10;function ct(i){if(H.length<ut){const e=H.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const s=ve.createWorker(i||{});return H.push(s),s}const t=lt++%H.length;return H[t]}class ve{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){dt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new ve(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Ze();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=ee(o).ktx2Loader.workerConfig;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());const n={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function dt(i){for(const t of i.geometries){const e=t.geometry,s=new U;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(ge(o))}for(const o in e.attributes){const r=e.attributes[o],n=ge(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(n=>ge(n));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ie,s.boundingBox.min=new A(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new A(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new Le(new A(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new je(o,r,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new $(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),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 We(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ge(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new Ue(s,e.stride);t=new Fe(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new ze(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ht=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const X=N("gltf-progressive-gc"),fe=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class m{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const w=s.lodInfos.get(u.key);if(w&&w.lods){a.min_count=Math.min(a.min_count,w.lods.length),a.max_count=Math.max(a.max_count,w.lods.length);for(let p=0;p<w.lods.length;p++){const y=w.lods[p];y.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,y.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,y.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of j)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:h&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(w=>(w&&n.uniforms[l].value!=w&&(n.uniforms[l].value=w,n.uniformsNeedUpdate=!0),w));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],w=r[a];u&&u.isTexture===!0?l.push({material:s,slot:w,texture:u,level:e}):l.push({material:s,slot:w,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),r&&r!==n&&((h||X)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,r.uuid),r.dispose()),null}if(this.trackTextureUsage(r),n&&n!==r&&this.untrackTextureUsage(n)&&(h||X)){const l=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} \u2192 ${e} for ${s.name||s.type}.${o}`,n.uuid)}s[o]=r}return r}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,m.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&m.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&m.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;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)}e.source&&(e.source[fe]=r);const n=r.guid;m.assignLODInformation(t,e,n,s,o),m.lodInfos.set(n,r),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),m.assignLODInformation(t,l,e,o,r),m.lodInfos.set(e,n);let a=m.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],m.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Ce(s,l);for(const u of j)u.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(h||X)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
- ${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(h||X)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} \u2192 ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||X)&&r("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const o=s-1;if(o<=0)return this.textureRefCounts.delete(e),(h||X)&&r("[gltf-progressive] Memory: Dispose texture",o),t.dispose(),!0;return this.textureRefCounts.set(e,o),h==="verbose"&&r("[gltf-progressive] Memory: Untrack texture",o),!1;function r(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const w=a&&u?`${a}x${u}`:"N/A";let p="N/A";a&&u&&(p=`~${(ot(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} \u2014 ${t.name} ${w} (${p}), refCount: ${s} \u2192 ${l}
3
- ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[fe]&&(n=l.source[fe])}if(n||(n=m.lodInfos.get(r)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let u=!1;const w=Array.isArray(n.lods);if(w&&e>=n.lods.length?u=!0:w||(u=!0),u){const p=this.lowresCache.get(r);if(p){const y=p.deref();if(y)return y;this.lowresCache.delete(r),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=st(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,w=await this.queue.slot(a),p=this.cache.get(u);if(p!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),p instanceof WeakRef){const c=p.deref();if(c){let x=c,L=!1;if(x instanceof $&&t instanceof $?x.image?.data||x.source?.data?x=this.copySettings(t,x):L=!0:x instanceof U&&t instanceof U&&(x.attributes.position?.array||(L=!0)),!L)return x}this.cache.delete(u),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await p.catch(L=>(console.error(`Error loading LOD ${e} from ${a}
4
- `,L),null)),x=!1;if(c==null||(c instanceof $&&t instanceof $?c.image?.data||c.source?.data?c=this.copySettings(t,c):(x=!0,this.cache.delete(u)):c instanceof U&&t instanceof U&&(c.attributes.position?.array||(x=!0,this.cache.delete(u)))),!x)return c}if(!w.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const y=n,P=new Promise(async(c,x)=>{if(ht){const g=await(await ct({})).load(a);if(g.textures.length>0)for(const d of g.textures){let f=d.texture;return m.assignLODInformation(o.url,f,r,e,void 0),t instanceof $&&(f=this.copySettings(t,f)),f&&(f.guid=y.guid),c(f)}if(g.geometries.length>0){const d=new Array;for(const f of g.geometries){const _=f.geometry;m.assignLODInformation(o.url,_,r,e,f.primitiveIndex),d.push(_)}return c(d)}return c(null)}const L=new ae;le(L),h&&(await new Promise(g=>setTimeout(g,1e3)),s&&console.warn("Start loading (delayed) "+a,y.guid));let C=a;if(y&&Array.isArray(y.lods)){const g=y.lods[e];g.hash&&(C+="?v="+g.hash)}const M=await L.loadAsync(C).catch(g=>(console.error(`Error loading LOD ${e} from ${a}
5
- `,g),c(null)));if(!M)return c(null);const R=M.parser;s&&console.log("Loading finished "+a,y.guid);let D=0;if(M.parser.json.textures){let g=!1;for(const d of M.parser.json.textures){if(d?.extensions){const f=d?.extensions[B];if(f?.guid&&f.guid===y.guid){g=!0;break}}D++}if(g){let d=await R.getDependency("texture",D);return d&&m.assignLODInformation(o.url,d,r,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,D,d,u),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=y.guid),c(d)}else h&&console.warn("Could not find texture with guid",y.guid,M.parser.json)}if(D=0,M.parser.json.meshes){let g=!1;for(const d of M.parser.json.meshes){if(d?.extensions){const f=d?.extensions[B];if(f?.guid&&f.guid===y.guid){g=!0;break}}D++}if(g){const d=await R.getDependency("mesh",D);if(s&&console.log(`Loaded Mesh "${d.name}"`,a,D,d,u),d.isMesh===!0){const f=d.geometry;return m.assignLODInformation(o.url,f,r,e,0),c(f)}else{const f=new Array;for(let _=0;_<d.children.length;_++){const S=d.children[_];if(S.isMesh===!0){const G=S.geometry;m.assignLODInformation(o.url,G,r,e,_),f.push(G)}}return c(f)}}else h&&console.warn("Could not find mesh with guid",y.guid,M.parser.json)}return c(null)});this.cache.set(u,P),w.use(P);const b=await P;return b!=null?b instanceof $?(this.cache.set(u,new WeakRef(b)),m._resourceRegistry.register(b,u)):Array.isArray(b)?this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(null)),b}else if(t instanceof $){s&&console.log("Load texture from uri: "+a);const u=await new qe().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,s&&console.log(n,u)):h&&console.warn("failed loading",a),u}}return null}static maxConcurrent=50;static queue=new rt(m.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const n=new gt(t,s,o,r);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
1
+ import{BufferGeometry as U,Mesh as F,Box3 as ie,Vector3 as A,Sphere as _e,CompressedTexture as je,Texture as $,Matrix3 as We,InterleavedBuffer as Ue,InterleavedBufferAttribute as Fe,BufferAttribute as ze,TextureLoader as qe,Matrix4 as be,Clock as Ve,MeshStandardMaterial as Xe}from"three";import{GLTFLoader as ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ke}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ye}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const Me="";globalThis.GLTF_PROGRESSIVE_VERSION=Me,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Qe=k,Je=z,De=new URL(k+"draco_decoder.js");De.searchParams.append("range","true"),fetch(De,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Qe&&Oe("./include/draco/"),z===Je&&Se("./include/ktx2/")}).finally(()=>{Te()});const Ze=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function Oe(i){k=i,T&&T[ue]!=k?(console.debug("Updating Draco decoder path to "+i),T[ue]=k,T.setDecoderPath(k),T.preload()):console.debug("Setting Draco decoder path to "+i)}function Se(i){z=i,I&&I.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),I.setTranscoderPath(z),I.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Te(),i?I.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:I,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(I),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let T,te,I;function Te(){T||(T=new Ye,T[ue]=k,T.setDecoderPath(k),T.setDecoderConfig({type:"js"}),T.preload()),I||(I=new He,I.setTranscoderPath(z),I.init()),te||(te=Ke)}const ce=new WeakMap;function de(i,t){let e=ce.get(i);e?e=Object.assign(e,t):e=t,ce.set(i,e)}const et=ae.prototype.load;function tt(...i){const t=ce.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return i[0]=e,et?.call(this,...i)}ae.prototype.load=tt,N("debugprogressive");function N(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function st(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function he(){return se!==void 0||(se=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",se)),se}let se;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class rt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}function ot(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,o=Math.floor(Math.log2(Math.max(t,e,s)))+1,r=nt(i);return t*e*s*r*(1-Math.pow(.25,o))/(1-.25)}function nt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const o=i.type;return o===1009||o===1010?s=1:o===1011||o===1012?s=2:o===1013||o===1014||o===1015?s=4:o===1016&&(s=2),t*s}const it=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function V(i){return i?.[ge]instanceof U?i[ge]:null}function Ce(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=at(t);e.userData={isRaycastMesh:!0},i[ge]=e}}function Ae(i=!0){if(i){if(K)return;const t=K=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!K)return;F.prototype.raycast=K,K=null}}let K=null;function at(i){const t=new U;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const j=new Array,h=N("debugprogressive");let Y,q=-1;if(h){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Y=!Y,console.log(`Toggle wireframe [${Y}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function ke(i){if(h&&Y!==void 0)if(Array.isArray(i))for(const t of i)ke(t);else i&&"wireframe"in i&&(i.wireframe=Y===!0)}const H=new Array;let lt=0;const ut=he()?2:10;function ct(i){if(H.length<ut){const e=H.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const s=Le.createWorker(i||{});return H.push(s),s}const t=lt++%H.length;return H[t]}class Le{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){dt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Le(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Ze();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=ee(o).ktx2Loader.workerConfig;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());const n={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function dt(i){for(const t of i.geometries){const e=t.geometry,s=new U;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(fe(o))}for(const o in e.attributes){const r=e.attributes[o],n=fe(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(n=>fe(n));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ie,s.boundingBox.min=new A(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new A(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new _e(new A(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new je(o,r,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new $(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),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 We(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function fe(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new Ue(s,e.stride);t=new Fe(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new ze(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ht=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const X=N("gltf-progressive-gc"),me=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class f{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const y=s.lodInfos.get(u.key);if(y&&y.lods){a.min_count=Math.min(a.min_count,y.lods.length),a.max_count=Math.max(a.max_count,y.lods.length);for(let p=0;p<y.lods.length;p++){const w=y.lods[p];w.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,w.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,w.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of j)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,f.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:h&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(y=>(y&&n.uniforms[l].value!=y&&(n.uniforms[l].value=y,n.uniformsNeedUpdate=!0),y));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],y=r[a];u&&u.isTexture===!0?l.push({material:s,slot:y,texture:u,level:e}):l.push({material:s,slot:y,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):f.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),r&&r!==n&&((h||X)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,r.uuid),r.dispose()),null}if(this.trackTextureUsage(r),n&&n!==r&&this.untrackTextureUsage(n)&&(h||X)){const l=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} \u2192 ${e} for ${s.name||s.type}.${o}`,n.uuid)}s[o]=r}return r}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&f.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,f.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&f.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&f.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;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)}e.source&&(e.source[me]=r);const n=r.guid;f.assignLODInformation(t,e,n,s,o),f.lodInfos.set(n,r),f.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),f.assignLODInformation(t,l,e,o,r),f.lodInfos.set(e,n);let a=f.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],f.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Ce(s,l);for(const u of j)u.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(h||X)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
+ ${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(h||X)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} \u2192 ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||X)&&r("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const o=s-1;if(o<=0)return this.textureRefCounts.delete(e),(h||X)&&r("[gltf-progressive] Memory: Dispose texture",o),t.dispose(),!0;return this.textureRefCounts.set(e,o),h==="verbose"&&r("[gltf-progressive] Memory: Untrack texture",o),!1;function r(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const y=a&&u?`${a}x${u}`:"N/A";let p="N/A";a&&u&&(p=`~${(ot(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} \u2014 ${t.name} ${y} (${p}), refCount: ${s} \u2192 ${l}
3
+ ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[me]&&(n=l.source[me])}if(n||(n=f.lodInfos.get(r)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let u=!1;const y=Array.isArray(n.lods);if(y&&e>=n.lods.length?u=!0:y||(u=!0),u){const p=this.lowresCache.get(r);if(p){const w=p.deref();if(w)return w;this.lowresCache.delete(r),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=st(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,y=await this.queue.slot(a),p=this.cache.get(u);if(p!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),p instanceof WeakRef){const c=p.deref();if(c){let x=c,L=!1;if(x instanceof $&&t instanceof $?x.image?.data||x.source?.data?x=this.copySettings(t,x):L=!0:x instanceof U&&t instanceof U&&(x.attributes.position?.array||(L=!0)),!L)return x}this.cache.delete(u),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await p.catch(L=>(console.error(`Error loading LOD ${e} from ${a}
4
+ `,L),null)),x=!1;if(c==null||(c instanceof $&&t instanceof $?c.image?.data||c.source?.data?c=this.copySettings(t,c):(x=!0,this.cache.delete(u)):c instanceof U&&t instanceof U&&(c.attributes.position?.array||(x=!0,this.cache.delete(u)))),!x)return c}if(!y.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const w=n,P=new Promise(async(c,x)=>{if(ht){const g=await(await ct({})).load(a);if(g.textures.length>0)for(const d of g.textures){let m=d.texture;return f.assignLODInformation(o.url,m,r,e,void 0),t instanceof $&&(m=this.copySettings(t,m)),m&&(m.guid=w.guid),c(m)}if(g.geometries.length>0){const d=new Array;for(const m of g.geometries){const _=m.geometry;f.assignLODInformation(o.url,_,r,e,m.primitiveIndex),d.push(_)}return c(d)}return c(null)}const L=new ae;le(L),h&&(await new Promise(g=>setTimeout(g,1e3)),s&&console.warn("Start loading (delayed) "+a,w.guid));let C=a;if(w&&Array.isArray(w.lods)){const g=w.lods[e];g.hash&&(C+="?v="+g.hash)}const M=await L.loadAsync(C).catch(g=>(console.error(`Error loading LOD ${e} from ${a}
5
+ `,g),c(null)));if(!M)return c(null);const R=M.parser;s&&console.log("Loading finished "+a,w.guid);let D=0;if(M.parser.json.textures){let g=!1;for(const d of M.parser.json.textures){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){let d=await R.getDependency("texture",D);return d&&f.assignLODInformation(o.url,d,r,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,D,d,u),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=w.guid),c(d)}else h&&console.warn("Could not find texture with guid",w.guid,M.parser.json)}if(D=0,M.parser.json.meshes){let g=!1;for(const d of M.parser.json.meshes){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){const d=await R.getDependency("mesh",D);if(s&&console.log(`Loaded Mesh "${d.name}"`,a,D,d,u),d.isMesh===!0){const m=d.geometry;return f.assignLODInformation(o.url,m,r,e,0),c(m)}else{const m=new Array;for(let _=0;_<d.children.length;_++){const S=d.children[_];if(S.isMesh===!0){const G=S.geometry;f.assignLODInformation(o.url,G,r,e,_),m.push(G)}}return c(m)}}else h&&console.warn("Could not find mesh with guid",w.guid,M.parser.json)}return c(null)});this.cache.set(u,P),y.use(P);const b=await P;return b!=null?b instanceof $?(this.cache.set(u,new WeakRef(b)),f._resourceRegistry.register(b,u)):Array.isArray(b)?this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(null)),b}else if(t instanceof $){s&&console.log("Load texture from uri: "+a);const u=await new qe().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,s&&console.log(n,u)):h&&console.warn("failed loading",a),u}}return null}static queue=new rt(he()?20:50,{debug:h!=!1});static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const n=new gt(t,s,o,r);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
6
6
  `,t.uuid,`
7
- `,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}}class gt{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class me{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=N("debugprogressive"),ft=N("noprogressive"),pe=Symbol("Needle:LODSManager"),xe=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static getObjectLODState(t){return t[xe]}static addPlugin(t){j.push(t)}static removePlugin(t){const e=j.indexOf(t);e>=0&&j.splice(e,1)}static get(t,e){if(t[pe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[pe];const s=new v(t,{engine:"unknown",...e});return t[pe]=s,s}renderer;context;projectionScreenMatrix=new _e;get plugins(){return j}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new me(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Ve;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ee(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,E&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){E&&(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)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(E==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const w=Math.random()*16777215,p=new Xe({color:w});a.object.material=p}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[xe];if(r||(r=new mt,s[xe]=r),r.frames++<2)return;for(const l of j)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;n>=0?(O.mesh_lod=n,O.texture_lod=n):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,n),h&&s.material&&!s.isGizmo&&ke(s.material);for(const l of j)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=m.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});me.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,n=m.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return me.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new Le;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new _e;_tempWorldPosition=new A;_tempBoxSize=new A;_tempBox2Size=new A;static corner0=new A;static corner1=new A;static corner2=new A;static corner3=new A;static _tempPtInside=new A;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=11,l=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=m.getMeshLODExtension(e.geometry)?.lods,u=m.getPrimitiveIndex(e.geometry),w=a&&a.length>0,p=m.getMaterialMinMaxLODsCount(e.material),y=p.min_count!==1/0&&p.min_count>=0&&p.max_count>=0;if(!w&&!y){r.mesh_lod=0,r.texture_lod=0;return}w||(l=!0,n=0);const P=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let b=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[v.$skinnedMeshBoundsOffset]){const L=v.skinnedMeshBoundsFrameOffsetCounter++;c[v.$skinnedMeshBoundsOffset]=L}const x=c[v.$skinnedMeshBoundsOffset];if((s.frames+x)%this.skinnedMeshAutoUpdateBoundsInterval===0){const L=V(c),C=c.geometry;L&&(c.geometry=L),c.computeBoundingBox(),c.geometry=C}}b=c.boundingBox}if(b){const c=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(b),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const g=this._tempBox.min,d=this._tempBox.max;let f=g.x,_=g.y,S=d.x,G=d.y;const Q=2,oe=1.5,J=(g.x+d.x)*.5,Z=(g.y+d.y)*.5;f=(f-J)*Q+J,_=(_-Z)*Q+Z,S=(S-J)*Q+J,G=(G-Z)*Q+Z;const Ge=f<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(d.x)),Ne=_<0&&G>0?0:Math.min(Math.abs(g.y),Math.abs(d.y)),ne=Math.max(Ge,Ne);s.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else s.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&P>0&&x.multiplyScalar(P/screen.availHeight),t.isPerspectiveCamera?x.x*=t.aspect:t.isOrthographicCamera;const L=t.matrixWorldInverse,C=this._tempBox2;C.copy(b),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(L);const M=C.getSize(this._tempBox2Size),R=Math.max(M.x,M.y);if(Math.max(x.x,x.y)!=0&&R!=0&&(x.z=M.z/Math.max(M.x,M.y)*Math.max(x.x,x.y)),s.lastScreenCoverage=Math.max(x.x,x.y,x.z),s.lastScreenspaceVolume.copy(x),s.lastScreenCoverage*=s.lastCentrality,E&&v.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const d=v.corner0,f=v.corner1,_=v.corner2,S=v.corner3;d.copy(this._tempBox.min),f.copy(this._tempBox.max),f.x=d.x,_.copy(this._tempBox.max),_.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=f.z=_.z=S.z=G,d.applyMatrix4(g),f.applyMatrix4(g),_.applyMatrix4(g),S.applyMatrix4(g),v.debugDrawLine(d,f,255),v.debugDrawLine(d,_,255),v.debugDrawLine(f,S,255),v.debugDrawLine(_,S,255)}let D=999;if(a&&s.lastScreenCoverage>0)for(let g=0;g<a.length;g++){const d=a[g],f=(d.densities?.[u]||d.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!d.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.")),f<o){D=g;break}}D<n&&(n=D,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,E&&r.mesh_lod!=s.lastLodLevel_Mesh){const c=a?.[r.mesh_lod];c&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(y){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=p.max_count-1,E){const x=p.lods[p.max_count-1];E&&console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`)}}else{const x=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let L=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(L*=1.5);const C=P/window.devicePixelRatio*L;let M=!1;for(let R=p.lods.length-1;R>=0;R--){const D=p.lods[R];if(!(c&&D.max_height>=2048)&&!(Te()&&D.max_height>4096)&&(D.max_height>C||!M&&R===0)){if(M=!0,r.texture_lod=R,E&&r.texture_lod<s.lastLodLevel_Texture){const g=D.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${g}px
7
+ `,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}}class gt{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class pe{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=N("debugprogressive"),ft=N("noprogressive"),xe=Symbol("Needle:LODSManager"),ye=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static getObjectLODState(t){return t[ye]}static addPlugin(t){j.push(t)}static removePlugin(t){const e=j.indexOf(t);e>=0&&j.splice(e,1)}static get(t,e){if(t[xe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[xe];const s=new v(t,{engine:"unknown",...e});return t[xe]=s,s}renderer;context;projectionScreenMatrix=new be;get plugins(){return j}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new pe(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Ve;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ee(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,E&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){E&&(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)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(E==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const y=Math.random()*16777215,p=new Xe({color:y});a.object.material=p}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[ye];if(r||(r=new mt,s[ye]=r),r.frames++<2)return;for(const l of j)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;n>=0?(O.mesh_lod=n,O.texture_lod=n):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,n),h&&s.material&&!s.isGizmo&&ke(s.material);for(const l of j)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=f.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,n=f.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return pe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new _e;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new be;_tempWorldPosition=new A;_tempBoxSize=new A;_tempBox2Size=new A;static corner0=new A;static corner1=new A;static corner2=new A;static corner3=new A;static _tempPtInside=new A;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=11,l=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),y=a&&a.length>0,p=f.getMaterialMinMaxLODsCount(e.material),w=p.min_count!==1/0&&p.min_count>=0&&p.max_count>=0;if(!y&&!w){r.mesh_lod=0,r.texture_lod=0;return}y||(l=!0,n=0);const P=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let b=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[v.$skinnedMeshBoundsOffset]){const L=v.skinnedMeshBoundsFrameOffsetCounter++;c[v.$skinnedMeshBoundsOffset]=L}const x=c[v.$skinnedMeshBoundsOffset];if((s.frames+x)%this.skinnedMeshAutoUpdateBoundsInterval===0){const L=V(c),C=c.geometry;L&&(c.geometry=L),c.computeBoundingBox(),c.geometry=C}}b=c.boundingBox}if(b){const c=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(b),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const g=this._tempBox.min,d=this._tempBox.max;let m=g.x,_=g.y,S=d.x,G=d.y;const Q=2,oe=1.5,J=(g.x+d.x)*.5,Z=(g.y+d.y)*.5;m=(m-J)*Q+J,_=(_-Z)*Q+Z,S=(S-J)*Q+J,G=(G-Z)*Q+Z;const Ge=m<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(d.x)),Ne=_<0&&G>0?0:Math.min(Math.abs(g.y),Math.abs(d.y)),ne=Math.max(Ge,Ne);s.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else s.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&P>0&&x.multiplyScalar(P/screen.availHeight),t.isPerspectiveCamera?x.x*=t.aspect:t.isOrthographicCamera;const L=t.matrixWorldInverse,C=this._tempBox2;C.copy(b),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(L);const M=C.getSize(this._tempBox2Size),R=Math.max(M.x,M.y);if(Math.max(x.x,x.y)!=0&&R!=0&&(x.z=M.z/Math.max(M.x,M.y)*Math.max(x.x,x.y)),s.lastScreenCoverage=Math.max(x.x,x.y,x.z),s.lastScreenspaceVolume.copy(x),s.lastScreenCoverage*=s.lastCentrality,E&&v.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const d=v.corner0,m=v.corner1,_=v.corner2,S=v.corner3;d.copy(this._tempBox.min),m.copy(this._tempBox.max),m.x=d.x,_.copy(this._tempBox.max),_.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=m.z=_.z=S.z=G,d.applyMatrix4(g),m.applyMatrix4(g),_.applyMatrix4(g),S.applyMatrix4(g),v.debugDrawLine(d,m,255),v.debugDrawLine(d,_,255),v.debugDrawLine(m,S,255),v.debugDrawLine(_,S,255)}let D=999;if(a&&s.lastScreenCoverage>0)for(let g=0;g<a.length;g++){const d=a[g],m=(d.densities?.[u]||d.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!d.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.")),m<o){D=g;break}}D<n&&(n=D,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,E&&r.mesh_lod!=s.lastLodLevel_Mesh){const c=a?.[r.mesh_lod];c&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(w){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=p.max_count-1,E){const x=p.lods[p.max_count-1];E&&console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`)}}else{const x=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let L=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(L*=1.5);const C=P/window.devicePixelRatio*L;let M=!1;for(let R=p.lods.length-1;R>=0;R--){const D=p.lods[R];if(!(c&&D.max_height>=2048)&&!(he()&&D.max_height>4096)&&(D.max_height>C||!M&&R===0)){if(M=!0,r.texture_lod=R,E&&r.texture_lod<s.lastLodLevel_Texture){const g=D.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${g}px
8
8
  Screensize: ${C.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
9
- ${e.name}`)}break}}}}else r.texture_lod=0}}class mt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new A;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let we=null;function ye(){const i=pt();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),we?.disconnect(),we=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),we.observe(document,{childList:!0,subtree:!0}))}function pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),ye()}),null)}function Ee(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{$e(i)})}const Re=new WeakSet;let xt=0;function $e(i){if(!i||Re.has(i))return null;Re.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++xt+`
10
- `,i.getAttribute("src"));let t=null,e=null,s=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),n=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let o=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=v.get(t,{engine:"model-viewer"});return v.addPlugin(new wt),r.enable(),r.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class wt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[re]==!0)return;e[re]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let w=0;w<u.length;w++){const p=u[w],y=a[p];if(y?.isTexture===!0){const P=y.userData?.associations?.textures;if(P==null)continue;const b=s.parser.json.textures[P];if(!b){console.warn("Texture data not found for texture index "+P);continue}if(b?.extensions?.[B]){const c=b.extensions[B];c&&r&&m.registerTexture(r,y,c.lods.length,P,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ie]==!0)return;e[Ie]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[B];if(r&&o){const n=e.uuid;m.registerMesh(o,n,e,0,r.lods.length,r)}}}function Be(...i){let t,e,s,o;switch(i.length){case 2:[s,e]=i,o={};break;case 3:[s,e,o]=i;break;case 4:[t,e,s,o]=i;break;default:throw new Error("Invalid arguments")}ee(e),le(s),de(s,{progressive:!0,...o?.hints}),s.register(n=>new m(n));const r=v.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}if(ye(),!it){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:v,configureLoader:de,getRaycastMesh:V,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{B as EXTENSION_NAME,v as LODsManager,m as NEEDLE_progressive,be as VERSION,le as addDracoAndKTX2Loaders,de as configureLoader,ee as createLoaders,V as getRaycastMesh,ye as patchModelViewer,Ce as registerRaycastMesh,De as setDracoDecoderLocation,Oe as setKTX2TranscoderLocation,Be as useNeedleProgressive,Ae as useRaycastMeshes};
9
+ ${e.name}`)}break}}}}else r.texture_lod=0}}class mt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new A;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let we=null;function ve(){const i=pt();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),we?.disconnect(),we=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),we.observe(document,{childList:!0,subtree:!0}))}function pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),ve()}),null)}function Ee(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{$e(i)})}const Re=new WeakSet;let xt=0;function $e(i){if(!i||Re.has(i))return null;Re.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++xt+`
10
+ `,i.getAttribute("src"));let t=null,e=null,s=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),n=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let o=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=v.get(t,{engine:"model-viewer"});return v.addPlugin(new yt),r.enable(),r.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class yt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[re]==!0)return;e[re]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let y=0;y<u.length;y++){const p=u[y],w=a[p];if(w?.isTexture===!0){const P=w.userData?.associations?.textures;if(P==null)continue;const b=s.parser.json.textures[P];if(!b){console.warn("Texture data not found for texture index "+P);continue}if(b?.extensions?.[B]){const c=b.extensions[B];c&&r&&f.registerTexture(r,w,c.lods.length,P,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ie]==!0)return;e[Ie]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[B];if(r&&o){const n=e.uuid;f.registerMesh(o,n,e,0,r.lods.length,r)}}}function Be(...i){let t,e,s,o;switch(i.length){case 2:[s,e]=i,o={};break;case 3:[s,e,o]=i;break;case 4:[t,e,s,o]=i;break;default:throw new Error("Invalid arguments")}ee(e),le(s),de(s,{progressive:!0,...o?.hints}),s.register(n=>new f(n));const r=v.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}if(ve(),!it){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:v,configureLoader:de,getRaycastMesh:V,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{B as EXTENSION_NAME,v as LODsManager,f as NEEDLE_progressive,Me as VERSION,le as addDracoAndKTX2Loaders,de as configureLoader,ee as createLoaders,V as getRaycastMesh,ve as patchModelViewer,Ce as registerRaycastMesh,Oe as setDracoDecoderLocation,Se as setKTX2TranscoderLocation,Be as useNeedleProgressive,Ae as useRaycastMeshes};
@@ -1,10 +1,10 @@
1
- "use strict";var Ue=Object.create;var _e=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var ze=Object.getOwnPropertyNames;var Ne=Object.getPrototypeOf,Ve=Object.prototype.hasOwnProperty;var qe=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ze(t))!Ve.call(i,r)&&r!==e&&_e(i,r,{get:()=>t[r],enumerable:!(s=We(t,r))||s.enumerable});return i};var Ee=(i,t,e)=>(e=i!=null?Ue(Ne(i)):{},qe(t||!i||!i.__esModule?_e(e,"default",{value:i,enumerable:!0}):e,i));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js"),je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Xe=require("three/examples/jsm/loaders/DRACOLoader.js"),Ke=require("three/examples/jsm/loaders/KTX2Loader.js");var ne=typeof document<"u"?document.currentScript:null;const Oe="";globalThis.GLTF_PROGRESSIVE_VERSION=Oe;console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",q="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ye=I,He=q,Se=new URL(I+"draco_decoder.js");Se.searchParams.append("range","true");fetch(Se,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===Ye&&Te("./include/draco/"),q===He&&Pe("./include/ktx2/")}).finally(()=>{Ce()});const Qe=()=>({dracoDecoderPath:I,ktx2TranscoderPath:q});function Te(i){I=i,A&&A[he]!=I?(console.debug("Updating Draco decoder path to "+i),A[he]=I,A.setDecoderPath(I),A.preload()):console.debug("Setting Draco decoder path to "+i)}function Pe(i){q=i,$&&$.transcoderPath!=q?(console.debug("Updating KTX2 transcoder path to "+i),$.setTranscoderPath(q),$.init()):console.debug("Setting KTX2 transcoder path to "+i)}function re(i){return Ce(),i?$.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:$,meshoptDecoder:se}}function ye(i){i.dracoLoader||i.setDRACOLoader(A),i.ktx2Loader||i.setKTX2Loader($),i.meshoptDecoder||i.setMeshoptDecoder(se)}const he=Symbol("dracoDecoderPath");let A,se,$;function Ce(){A||(A=new Xe.DRACOLoader,A[he]=I,A.setDecoderPath(I),A.setDecoderConfig({type:"js"}),A.preload()),$||($=new Ke.KTX2Loader,$.setTranscoderPath(q),$.init()),se||(se=je.MeshoptDecoder)}const ge=new WeakMap;function xe(i,t){let e=ge.get(i);e?e=Object.assign(e,t):e=t,ge.set(i,e)}const Je=me.GLTFLoader.prototype.load;function Ze(...i){const t=ge.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,n=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${n}`:this.requestHeader.Accept=`*/*;usecase=${n}`,e=s.toString()}return i[0]=e,Je?.call(this,...i)}me.GLTFLoader.prototype.load=Ze;z("debugprogressive");function z(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function et(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Ae(){return K!==void 0||(K=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",K)),K}let K;function ve(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class tt{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:r}=this._queue.shift();r({use:o=>this.add(s,o)})}}}function st(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,o=rt(i);return t*e*s*o*(1-Math.pow(.25,r))/(1-.25)}function rt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=i.type;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}const ot=typeof window>"u"&&typeof document>"u",pe=Symbol("needle:raycast-mesh");function j(i){return i?.[pe]instanceof c.BufferGeometry?i[pe]:null}function Re(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!j(i)){const s=it(t);s.userData={isRaycastMesh:!0},i[pe]=s}}function ke(i=!0){if(i){if(Y)return;const t=Y=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const r=this,o=j(r);let n;o&&r.isMesh&&(n=r.geometry,r.geometry=o),t.call(this,e,s),n&&(r.geometry=n)}}else{if(!Y)return;c.Mesh.prototype.raycast=Y,Y=null}}let Y=null;function it(i){const t=new c.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,h=z("debugprogressive");let Q,V=-1;if(h){let i=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(V=s,console.log(`Set LOD level to [${V}]`))})}function Ie(i){if(h&&Q!==void 0)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=Q===!0)}const H=new Array;let nt=0;const at=Ae()?2:10;function lt(i){if(H.length<at){const s=H.length;h&&console.warn(`[Worker] Creating new worker #${s}`);const r=we.createWorker(i||{});return H.push(r),r}const t=nt++%H.length;return H[t]}class we{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const o of this._running)if(o.url===r.result.url){ut(r.result),o.resolve(r.result);const n=o.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ne&&ne.tagName.toUpperCase()==="SCRIPT"&&ne.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new we(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Qe();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),r=await this._webglRenderer);const l=re(r).ktx2Loader.workerConfig;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());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ut(i){for(const t of i.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ae(r))}for(const r in e.attributes){const o=e.attributes[r],n=ae(o);s.setAttribute(r,n)}if(e.morphAttributes)for(const r in e.morphAttributes){const n=e.morphAttributes[r].map(l=>ae(l));s.morphAttributes[r]=n}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(r,o,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),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 c.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ae(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,r=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(r,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new c.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ct=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const E=z("gltf-progressive-gc"),le=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class y{get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",o=t[r];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let x=0;x<d.lods.length;x++){const L=d.lods[x];L.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,L.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,L.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const n=t[o];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const o=s.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const o of W)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,y.getOrLoadLOD(s,e).then(o=>{if(Array.isArray(o)){const n=r.index||0;o=o[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&s!=o&&(o?.isBufferGeometry?t.geometry=o:h&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const r=new Array;for(const o of s.material){const n=this.assignTextureLOD(o,e);r.push(n)}return Promise.all(r).then(o=>{const n=new Array;for(const l of o)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,r=[],o=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(d=>(d&&n.uniforms[l].value!=d&&(n.uniforms[l].value=d,n.uniformsNeedUpdate=!0),d));r.push(u),o.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);r.push(a),o.push(n)}}return Promise.all(r).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],d=o[a];u&&u.isTexture===!0?l.push({material:s,slot:d,texture:u,level:e}):l.push({material:s,slot:d,texture:null,level:e})}return l})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,r){return t?.isTexture!==!0?Promise.resolve(null):r==="glyphMap"?Promise.resolve(t):y.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(o?.isTexture===!0){if(o!=t&&s&&r){const n=s[r];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,o),o&&o!==n&&((h||E)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,o.uuid),o.dispose()),null}if(this.trackTextureUsage(o),n&&n!==o&&this.untrackTextureUsage(n)&&(h||E)){const a=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level??"?"} → ${e} for ${s.name||s.type}.${r}`,n.uuid)}s[r]=o}return o}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&y.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r){if(!r.lods){h&&console.warn("Texture has no LODs",r);return}let o=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(o=!0,y.registerTexture(this.url,n,r.lods?.length,s,r));o||this.parser.getDependency("texture",s).then(n=>{n&&y.registerTexture(this.url,n,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r&&r.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const n=this.parser.associations.get(o);n?.meshes===s&&y.registerMesh(this.url,r.guid,o,r.lods.length,n.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,o)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[le]=o);const n=o.guid;y.assignLODInformation(t,e,n,s,r),y.lodInfos.set(n,o),y.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,r,o,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:o,uuid:s.uuid},n,s),y.assignLODInformation(t,l,e,r,o),y.lodInfos.set(e,n);let u=y.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],y.lowresCache.set(e,new WeakRef(u)),r>0&&!j(s)&&Re(s,l);for(const d of W)d.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=y.cache.get(t);(h||E)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
- ${t}`),e instanceof WeakRef&&(e.deref()||(y.cache.delete(t),(h||E)&&console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||E)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const r=s-1;if(r<=0)return this.textureRefCounts.delete(e),(h||E)&&o("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),h==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",r),!1;function o(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const d=a&&u?`${a}x${u}`:"N/A";let x="N/A";a&&u&&(x=`~${(st(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} — ${t.name} ${d} (${x}), refCount: ${s} → ${l}
3
- ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",r=this.getAssignedLODInformation(t);if(!r)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const o=r?.key;let n;if(t.isTexture===!0){const a=t;a.source&&a.source[le]&&(n=a.source[le])}if(n||(n=y.lodInfos.get(o)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`,t.type,y.lodInfos);else{if(e>0){let d=!1;const x=Array.isArray(n.lods);if(x&&e>=n.lods.length?d=!0:x||(d=!0),d){const L=this.lowresCache.get(o);if(L){const _=L.deref();if(_)return _;this.lowresCache.delete(o),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`)}return null}}const a=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!a)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const u=et(r.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const d=u+"_"+n.guid,x=await this.queue.slot(u),L=this.cache.get(d);if(L!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${d}`),L instanceof WeakRef){const f=L.deref();if(f){let p=f,b=!1;if(p instanceof c.Texture&&t instanceof c.Texture?p.image?.data||p.source?.data?p=this.copySettings(t,p):b=!0:p instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(p.attributes.position?.array||(b=!0)),!b)return p}this.cache.delete(d),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${d}`)}else{let f=await L.catch(b=>(console.error(`Error loading LOD ${e} from ${u}
4
- `,b),null)),p=!1;if(f==null||(f instanceof c.Texture&&t instanceof c.Texture?f.image?.data||f.source?.data?f=this.copySettings(t,f):(p=!0,this.cache.delete(d)):f instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(f.attributes.position?.array||(p=!0,this.cache.delete(d)))),!p)return f}if(!x.use)return h&&console.log(`LOD ${e} was aborted: ${u}`),null;const _=n,R=new Promise(async(f,p)=>{if(ct){const w=await(await lt({})).load(u);if(w.textures.length>0)for(const g of w.textures){let m=g.texture;return y.assignLODInformation(r.url,m,o,e,void 0),t instanceof c.Texture&&(m=this.copySettings(t,m)),m&&(m.guid=_.guid),f(m)}if(w.geometries.length>0){const g=new Array;for(const m of w.geometries){const S=m.geometry;y.assignLODInformation(r.url,S,o,e,m.primitiveIndex),g.push(S)}return f(g)}return f(null)}const b=new me.GLTFLoader;ye(b),h&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+u,_.guid));let B=u;if(_&&Array.isArray(_.lods)){const v=_.lods[e];v.hash&&(B+="?v="+v.hash)}const O=await b.loadAsync(B).catch(v=>(console.error(`Error loading LOD ${e} from ${u}
5
- `,v),f(null)));if(!O)return f(null);const N=O.parser;s&&console.log("Loading finished "+u,_.guid);let P=0;if(O.parser.json.textures){let v=!1;for(const w of O.parser.json.textures){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){let w=await N.getDependency("texture",P);return w&&y.assignLODInformation(r.url,w,o,e,void 0),s&&console.log('change "'+t.name+'" → "'+w.name+'"',u,P,w,d),t instanceof c.Texture&&(w=this.copySettings(t,w)),w&&(w.guid=_.guid),f(w)}else h&&console.warn("Could not find texture with guid",_.guid,O.parser.json)}if(P=0,O.parser.json.meshes){let v=!1;for(const w of O.parser.json.meshes){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){const w=await N.getDependency("mesh",P);if(s&&console.log(`Loaded Mesh "${w.name}"`,u,P,w,d),w.isMesh===!0){const g=w.geometry;return y.assignLODInformation(r.url,g,o,e,0),f(g)}else{const g=new Array;for(let m=0;m<w.children.length;m++){const S=w.children[m];if(S.isMesh===!0){const T=S.geometry;y.assignLODInformation(r.url,T,o,e,m),g.push(T)}}return f(g)}}else h&&console.warn("Could not find mesh with guid",_.guid,O.parser.json)}return f(null)});this.cache.set(d,R),x.use(R);const D=await R;return D!=null?D instanceof c.Texture?(this.cache.set(d,new WeakRef(D)),y._resourceRegistry.register(D,d)):Array.isArray(D)?this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(null)),D}else if(t instanceof c.Texture){s&&console.log("Load texture from uri: "+u);const x=await new c.TextureLoader().loadAsync(u);return x?(x.guid=n.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,s&&console.log(n,x)):h&&console.warn("failed loading",u),x}}return null}static maxConcurrent=50;static queue=new tt(y.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,s,r,o){if(!e)return;e.userData||(e.userData={});const n=new dt(t,s,r,o);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
1
+ "use strict";var Ue=Object.create;var ve=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var ze=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var qe=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ze(t))!Ne.call(i,r)&&r!==e&&ve(i,r,{get:()=>t[r],enumerable:!(s=We(t,r))||s.enumerable});return i};var Ee=(i,t,e)=>(e=i!=null?Ue(Ve(i)):{},qe(t||!i||!i.__esModule?ve(e,"default",{value:i,enumerable:!0}):e,i));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js"),je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Xe=require("three/examples/jsm/loaders/DRACOLoader.js"),Ke=require("three/examples/jsm/loaders/KTX2Loader.js");var ne=typeof document<"u"?document.currentScript:null;const Se="";globalThis.GLTF_PROGRESSIVE_VERSION=Se;console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",q="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ye=I,He=q,Te=new URL(I+"draco_decoder.js");Te.searchParams.append("range","true");fetch(Te,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===Ye&&Pe("./include/draco/"),q===He&&Ce("./include/ktx2/")}).finally(()=>{Ae()});const Qe=()=>({dracoDecoderPath:I,ktx2TranscoderPath:q});function Pe(i){I=i,A&&A[he]!=I?(console.debug("Updating Draco decoder path to "+i),A[he]=I,A.setDecoderPath(I),A.preload()):console.debug("Setting Draco decoder path to "+i)}function Ce(i){q=i,$&&$.transcoderPath!=q?(console.debug("Updating KTX2 transcoder path to "+i),$.setTranscoderPath(q),$.init()):console.debug("Setting KTX2 transcoder path to "+i)}function re(i){return Ae(),i?$.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:$,meshoptDecoder:se}}function ye(i){i.dracoLoader||i.setDRACOLoader(A),i.ktx2Loader||i.setKTX2Loader($),i.meshoptDecoder||i.setMeshoptDecoder(se)}const he=Symbol("dracoDecoderPath");let A,se,$;function Ae(){A||(A=new Xe.DRACOLoader,A[he]=I,A.setDecoderPath(I),A.setDecoderConfig({type:"js"}),A.preload()),$||($=new Ke.KTX2Loader,$.setTranscoderPath(q),$.init()),se||(se=je.MeshoptDecoder)}const ge=new WeakMap;function xe(i,t){let e=ge.get(i);e?e=Object.assign(e,t):e=t,ge.set(i,e)}const Je=me.GLTFLoader.prototype.load;function Ze(...i){const t=ge.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,n=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${n}`:this.requestHeader.Accept=`*/*;usecase=${n}`,e=s.toString()}return i[0]=e,Je?.call(this,...i)}me.GLTFLoader.prototype.load=Ze;z("debugprogressive");function z(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function et(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function we(){return K!==void 0||(K=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",K)),K}let K;function Me(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class tt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:r}=this._queue.shift();r({use:o=>this.add(s,o)})}}}function st(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,o=rt(i);return t*e*s*o*(1-Math.pow(.25,r))/(1-.25)}function rt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=i.type;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}const ot=typeof window>"u"&&typeof document>"u",pe=Symbol("needle:raycast-mesh");function j(i){return i?.[pe]instanceof c.BufferGeometry?i[pe]:null}function Re(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!j(i)){const s=it(t);s.userData={isRaycastMesh:!0},i[pe]=s}}function ke(i=!0){if(i){if(Y)return;const t=Y=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const r=this,o=j(r);let n;o&&r.isMesh&&(n=r.geometry,r.geometry=o),t.call(this,e,s),n&&(r.geometry=n)}}else{if(!Y)return;c.Mesh.prototype.raycast=Y,Y=null}}let Y=null;function it(i){const t=new c.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,h=z("debugprogressive");let Q,N=-1;if(h){let i=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(N=s,console.log(`Set LOD level to [${N}]`))})}function Ie(i){if(h&&Q!==void 0)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=Q===!0)}const H=new Array;let nt=0;const at=we()?2:10;function lt(i){if(H.length<at){const s=H.length;h&&console.warn(`[Worker] Creating new worker #${s}`);const r=Le.createWorker(i||{});return H.push(r),r}const t=nt++%H.length;return H[t]}class Le{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const o of this._running)if(o.url===r.result.url){ut(r.result),o.resolve(r.result);const n=o.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ne&&ne.tagName.toUpperCase()==="SCRIPT"&&ne.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new Le(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Qe();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),r=await this._webglRenderer);const l=re(r).ktx2Loader.workerConfig;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());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ut(i){for(const t of i.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ae(r))}for(const r in e.attributes){const o=e.attributes[r],n=ae(o);s.setAttribute(r,n)}if(e.morphAttributes)for(const r in e.morphAttributes){const n=e.morphAttributes[r].map(l=>ae(l));s.morphAttributes[r]=n}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(r,o,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),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 c.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ae(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,r=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(r,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new c.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ct=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const E=z("gltf-progressive-gc"),le=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class m{get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",o=t[r];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let x=0;x<d.lods.length;x++){const L=d.lods[x];L.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,L.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,L.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const n=t[o];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const o=s.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const o of W)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,e).then(o=>{if(Array.isArray(o)){const n=r.index||0;o=o[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&s!=o&&(o?.isBufferGeometry?t.geometry=o:h&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const r=new Array;for(const o of s.material){const n=this.assignTextureLOD(o,e);r.push(n)}return Promise.all(r).then(o=>{const n=new Array;for(const l of o)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,r=[],o=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(d=>(d&&n.uniforms[l].value!=d&&(n.uniforms[l].value=d,n.uniformsNeedUpdate=!0),d));r.push(u),o.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);r.push(a),o.push(n)}}return Promise.all(r).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],d=o[a];u&&u.isTexture===!0?l.push({material:s,slot:d,texture:u,level:e}):l.push({material:s,slot:d,texture:null,level:e})}return l})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r){return t?.isTexture!==!0?Promise.resolve(null):r==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(o?.isTexture===!0){if(o!=t&&s&&r){const n=s[r];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,o),o&&o!==n&&((h||E)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,o.uuid),o.dispose()),null}if(this.trackTextureUsage(o),n&&n!==o&&this.untrackTextureUsage(n)&&(h||E)){const a=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level??"?"} → ${e} for ${s.name||s.type}.${r}`,n.uuid)}s[r]=o}return o}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r){if(!r.lods){h&&console.warn("Texture has no LODs",r);return}let o=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(o=!0,m.registerTexture(this.url,n,r.lods?.length,s,r));o||this.parser.getDependency("texture",s).then(n=>{n&&m.registerTexture(this.url,n,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r&&r.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const n=this.parser.associations.get(o);n?.meshes===s&&m.registerMesh(this.url,r.guid,o,r.lods.length,n.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,o)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[le]=o);const n=o.guid;m.assignLODInformation(t,e,n,s,r),m.lodInfos.set(n,o),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,r,o,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:o,uuid:s.uuid},n,s),m.assignLODInformation(t,l,e,r,o),m.lodInfos.set(e,n);let u=m.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],m.lowresCache.set(e,new WeakRef(u)),r>0&&!j(s)&&Re(s,l);for(const d of W)d.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(h||E)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
+ ${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(h||E)&&console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||E)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const r=s-1;if(r<=0)return this.textureRefCounts.delete(e),(h||E)&&o("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),h==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",r),!1;function o(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const d=a&&u?`${a}x${u}`:"N/A";let x="N/A";a&&u&&(x=`~${(st(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} — ${t.name} ${d} (${x}), refCount: ${s} → ${l}
3
+ ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",r=this.getAssignedLODInformation(t);if(!r)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const o=r?.key;let n;if(t.isTexture===!0){const a=t;a.source&&a.source[le]&&(n=a.source[le])}if(n||(n=m.lodInfos.get(o)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let d=!1;const x=Array.isArray(n.lods);if(x&&e>=n.lods.length?d=!0:x||(d=!0),d){const L=this.lowresCache.get(o);if(L){const _=L.deref();if(_)return _;this.lowresCache.delete(o),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`)}return null}}const a=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!a)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const u=et(r.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const d=u+"_"+n.guid,x=await this.queue.slot(u),L=this.cache.get(d);if(L!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${d}`),L instanceof WeakRef){const f=L.deref();if(f){let p=f,b=!1;if(p instanceof c.Texture&&t instanceof c.Texture?p.image?.data||p.source?.data?p=this.copySettings(t,p):b=!0:p instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(p.attributes.position?.array||(b=!0)),!b)return p}this.cache.delete(d),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${d}`)}else{let f=await L.catch(b=>(console.error(`Error loading LOD ${e} from ${u}
4
+ `,b),null)),p=!1;if(f==null||(f instanceof c.Texture&&t instanceof c.Texture?f.image?.data||f.source?.data?f=this.copySettings(t,f):(p=!0,this.cache.delete(d)):f instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(f.attributes.position?.array||(p=!0,this.cache.delete(d)))),!p)return f}if(!x.use)return h&&console.log(`LOD ${e} was aborted: ${u}`),null;const _=n,R=new Promise(async(f,p)=>{if(ct){const w=await(await lt({})).load(u);if(w.textures.length>0)for(const g of w.textures){let y=g.texture;return m.assignLODInformation(r.url,y,o,e,void 0),t instanceof c.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=_.guid),f(y)}if(w.geometries.length>0){const g=new Array;for(const y of w.geometries){const S=y.geometry;m.assignLODInformation(r.url,S,o,e,y.primitiveIndex),g.push(S)}return f(g)}return f(null)}const b=new me.GLTFLoader;ye(b),h&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+u,_.guid));let B=u;if(_&&Array.isArray(_.lods)){const v=_.lods[e];v.hash&&(B+="?v="+v.hash)}const O=await b.loadAsync(B).catch(v=>(console.error(`Error loading LOD ${e} from ${u}
5
+ `,v),f(null)));if(!O)return f(null);const V=O.parser;s&&console.log("Loading finished "+u,_.guid);let P=0;if(O.parser.json.textures){let v=!1;for(const w of O.parser.json.textures){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){let w=await V.getDependency("texture",P);return w&&m.assignLODInformation(r.url,w,o,e,void 0),s&&console.log('change "'+t.name+'" → "'+w.name+'"',u,P,w,d),t instanceof c.Texture&&(w=this.copySettings(t,w)),w&&(w.guid=_.guid),f(w)}else h&&console.warn("Could not find texture with guid",_.guid,O.parser.json)}if(P=0,O.parser.json.meshes){let v=!1;for(const w of O.parser.json.meshes){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){const w=await V.getDependency("mesh",P);if(s&&console.log(`Loaded Mesh "${w.name}"`,u,P,w,d),w.isMesh===!0){const g=w.geometry;return m.assignLODInformation(r.url,g,o,e,0),f(g)}else{const g=new Array;for(let y=0;y<w.children.length;y++){const S=w.children[y];if(S.isMesh===!0){const T=S.geometry;m.assignLODInformation(r.url,T,o,e,y),g.push(T)}}return f(g)}}else h&&console.warn("Could not find mesh with guid",_.guid,O.parser.json)}return f(null)});this.cache.set(d,R),x.use(R);const D=await R;return D!=null?D instanceof c.Texture?(this.cache.set(d,new WeakRef(D)),m._resourceRegistry.register(D,d)):Array.isArray(D)?this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(null)),D}else if(t instanceof c.Texture){s&&console.log("Load texture from uri: "+u);const x=await new c.TextureLoader().loadAsync(u);return x?(x.guid=n.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,s&&console.log(n,x)):h&&console.warn("failed loading",u),x}}return null}static queue=new tt(we()?20:50,{debug:h!=!1});static assignLODInformation(t,e,s,r,o){if(!e)return;e.userData||(e.userData={});const n=new dt(t,s,r,o);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
6
6
  `,t.uuid,`
7
- `,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}}class dt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ue{static addPromise=(t,e,s,r)=>{r.forEach(o=>{o.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let r=this._seen.get(e);if(r>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const k=z("debugprogressive"),ft=z("noprogressive"),ce=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),C={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const s=new M(t,{engine:"unknown",...e});return t[ce]=s,s}renderer;context;projectionScreenMatrix=new c.Matrix4;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ue(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(s);o>=0&&(this._newPromiseGroups.splice(o,1),ve()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new c.Clock;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;re(this.renderer),this.renderer.render=function(s,r){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.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));const n=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let n=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(ft||(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))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of r){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")){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)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const d=Math.random()*16777215,x=new c.MeshStandardMaterial({color:d});a.object.material=x}const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let o=s[de];if(o||(o=new ht,s[de]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;n>=0?(C.mesh_lod=n,C.texture_lod=n):(this.calculateLodLevel(e,s,o,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,n),h&&s.material&&!s.isGizmo&&Ie(s.material);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);o.lastLodLevel_Mesh=C.mesh_lod,o.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let r=!1;if((t[U]===void 0||e<t[U])&&(r=!0),s!==void 0&&s>=0&&(r=t[U]!=s,e=s),r){t[U]=e;const o=y.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,o,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[U]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[U]!=r,e=r),s){t[U]=e;const o=t.geometry,n=y.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ue.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,r=t.max,o=(s.x+r.x)*.5,n=(s.y+r.y)*.5;return this._tempPtInside.set(o,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let l=10+1,a=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=y.getMeshLODExtension(e.geometry)?.lods,d=y.getPrimitiveIndex(e.geometry),x=u&&u.length>0,L=y.getMaterialMinMaxLODsCount(e.material),_=L.min_count!==1/0&&L.min_count>=0&&L.max_count>=0;if(!x&&!_){o.mesh_lod=0,o.texture_lod=0;return}x||(a=!0,l=0);const R=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const p=f[M.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=j(f),B=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=B}}D=f.boundingBox}if(D){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const g=this._tempBox.min,m=this._tempBox.max;let S=g.x,T=g.y,G=m.x,X=m.y;const J=2,oe=1.5,Z=(g.x+m.x)*.5,ee=(g.y+m.y)*.5;S=(S-Z)*J+Z,T=(T-ee)*J+ee,G=(G-Z)*J+Z,X=(X-ee)*J+ee;const Ge=S<0&&G>0?0:Math.min(Math.abs(g.x),Math.abs(m.x)),Fe=T<0&&X>0?0:Math.min(Math.abs(g.y),Math.abs(m.y)),ie=Math.max(Ge,Fe);s.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&R>0&&p.multiplyScalar(R/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,B=this._tempBox2;B.copy(D),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(b);const O=B.getSize(this._tempBox2Size),N=Math.max(O.x,O.y);if(Math.max(p.x,p.y)!=0&&N!=0&&(p.z=O.z/Math.max(O.x,O.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,k&&M.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const m=M.corner0,S=M.corner1,T=M.corner2,G=M.corner3;m.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=m.x,T.copy(this._tempBox.max),T.y=m.y,G.copy(this._tempBox.max);const X=(m.z+G.z)*.5;m.z=S.z=T.z=G.z=X,m.applyMatrix4(g),S.applyMatrix4(g),T.applyMatrix4(g),G.applyMatrix4(g),M.debugDrawLine(m,S,255),M.debugDrawLine(m,T,255),M.debugDrawLine(S,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let g=0;g<u.length;g++){const m=u[g],T=(m.densities?.[d]||m.density||1e-5)/s.lastScreenCoverage;if(d>0&&ve()&&!m.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.")),T<r){v=g;break}}v<l&&(l=v,a=!0)}if(a?o.mesh_lod=l:o.mesh_lod=s.lastLodLevel_Mesh,k&&o.mesh_lod!=s.lastLodLevel_Mesh){const p=u?.[o.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${p.densities?.[d].toFixed(0)}) | ${e.name}`)}if(_){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(o.texture_lod=L.max_count-1,k){const p=L.lods[L.max_count-1];k&&console.log(`First Texture LOD ${o.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const O=R/window.devicePixelRatio*b;let N=!1;for(let P=L.lods.length-1;P>=0;P--){const v=L.lods[P];if(!(f&&v.max_height>=2048)&&!(Ae()&&v.max_height>4096)&&(v.max_height>O||!N&&P===0)){if(N=!0,o.texture_lod=P,k&&o.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${w}px
7
+ `,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}}class dt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ue{static addPromise=(t,e,s,r)=>{r.forEach(o=>{o.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let r=this._seen.get(e);if(r>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const k=z("debugprogressive"),ft=z("noprogressive"),ce=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),C={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const s=new M(t,{engine:"unknown",...e});return t[ce]=s,s}renderer;context;projectionScreenMatrix=new c.Matrix4;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ue(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(s);o>=0&&(this._newPromiseGroups.splice(o,1),Me()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new c.Clock;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;re(this.renderer),this.renderer.render=function(s,r){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.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));const n=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let n=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(ft||(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))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of r){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")){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)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const d=Math.random()*16777215,x=new c.MeshStandardMaterial({color:d});a.object.material=x}const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let o=s[de];if(o||(o=new ht,s[de]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;n>=0?(C.mesh_lod=n,C.texture_lod=n):(this.calculateLodLevel(e,s,o,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,n),h&&s.material&&!s.isGizmo&&Ie(s.material);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);o.lastLodLevel_Mesh=C.mesh_lod,o.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let r=!1;if((t[U]===void 0||e<t[U])&&(r=!0),s!==void 0&&s>=0&&(r=t[U]!=s,e=s),r){t[U]=e;const o=m.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,o,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[U]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[U]!=r,e=r),s){t[U]=e;const o=t.geometry,n=m.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ue.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,r=t.max,o=(s.x+r.x)*.5,n=(s.y+r.y)*.5;return this._tempPtInside.set(o,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let l=10+1,a=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,d=m.getPrimitiveIndex(e.geometry),x=u&&u.length>0,L=m.getMaterialMinMaxLODsCount(e.material),_=L.min_count!==1/0&&L.min_count>=0&&L.max_count>=0;if(!x&&!_){o.mesh_lod=0,o.texture_lod=0;return}x||(a=!0,l=0);const R=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const p=f[M.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=j(f),B=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=B}}D=f.boundingBox}if(D){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const g=this._tempBox.min,y=this._tempBox.max;let S=g.x,T=g.y,G=y.x,X=y.y;const J=2,oe=1.5,Z=(g.x+y.x)*.5,ee=(g.y+y.y)*.5;S=(S-Z)*J+Z,T=(T-ee)*J+ee,G=(G-Z)*J+Z,X=(X-ee)*J+ee;const Ge=S<0&&G>0?0:Math.min(Math.abs(g.x),Math.abs(y.x)),Fe=T<0&&X>0?0:Math.min(Math.abs(g.y),Math.abs(y.y)),ie=Math.max(Ge,Fe);s.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&R>0&&p.multiplyScalar(R/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,B=this._tempBox2;B.copy(D),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(b);const O=B.getSize(this._tempBox2Size),V=Math.max(O.x,O.y);if(Math.max(p.x,p.y)!=0&&V!=0&&(p.z=O.z/Math.max(O.x,O.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,k&&M.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const y=M.corner0,S=M.corner1,T=M.corner2,G=M.corner3;y.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=y.x,T.copy(this._tempBox.max),T.y=y.y,G.copy(this._tempBox.max);const X=(y.z+G.z)*.5;y.z=S.z=T.z=G.z=X,y.applyMatrix4(g),S.applyMatrix4(g),T.applyMatrix4(g),G.applyMatrix4(g),M.debugDrawLine(y,S,255),M.debugDrawLine(y,T,255),M.debugDrawLine(S,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let g=0;g<u.length;g++){const y=u[g],T=(y.densities?.[d]||y.density||1e-5)/s.lastScreenCoverage;if(d>0&&Me()&&!y.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.")),T<r){v=g;break}}v<l&&(l=v,a=!0)}if(a?o.mesh_lod=l:o.mesh_lod=s.lastLodLevel_Mesh,k&&o.mesh_lod!=s.lastLodLevel_Mesh){const p=u?.[o.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${p.densities?.[d].toFixed(0)}) | ${e.name}`)}if(_){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(o.texture_lod=L.max_count-1,k){const p=L.lods[L.max_count-1];k&&console.log(`First Texture LOD ${o.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const O=R/window.devicePixelRatio*b;let V=!1;for(let P=L.lods.length-1;P>=0;P--){const v=L.lods[P];if(!(f&&v.max_height>=2048)&&!(we()&&v.max_height>4096)&&(v.max_height>O||!V&&P===0)){if(V=!0,o.texture_lod=P,k&&o.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${w}px
8
8
  Screensize: ${O.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${p.toFixed(1)}
9
- ${e.name}`)}break}}}}else o.texture_lod=0}}class ht{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const Me=Symbol("NEEDLE_mesh_lod"),te=Symbol("NEEDLE_texture_lod");let fe=null;function Le(){const i=gt();i&&(i.mapURLs(function(t){return be(),t}),be(),fe?.disconnect(),fe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),fe.observe(document,{childList:!0,subtree:!0}))}function gt(){if(typeof customElements>"u")return null;const i=customElements.get("model-viewer");return i||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Le()}),null)}function be(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{$e(t)})}const De=new WeakSet;let pt=0;function $e(i){if(!i||De.has(i))return null;De.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++pt+`
10
- `,i.getAttribute("src"));let t=null,e=null,s=null;for(let r=i;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),n=o.find(u=>u.toString()=="Symbol(renderer)"),l=o.find(u=>u.toString()=="Symbol(scene)"),a=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let r=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=M.get(t,{engine:"model-viewer"});return M.addPlugin(new mt),o.enable(),o.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class mt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[te]==!0)return;e[te]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(o&&s&&e.material){let n=function(a){if(a[te]==!0)return;a[te]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let d=0;d<u.length;d++){const x=u[d],L=a[x];if(L?.isTexture===!0){const _=L.userData?.associations?.textures;if(_==null)continue;const R=s.parser.json.textures[_];if(!R){console.warn("Texture data not found for texture index "+_);continue}if(R?.extensions?.[F]){const D=R.extensions[F];D&&o&&y.registerTexture(o,L,D.lods.length,_,D)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Me]==!0)return;e[Me]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const o=e.userData?.gltfExtensions?.[F];if(o&&r){const n=e.uuid;y.registerMesh(r,n,e,0,o.lods.length,o)}}}function Be(...i){let t,e,s,r;switch(i.length){case 2:[s,e]=i,r={};break;case 3:[s,e,r]=i;break;case 4:[t,e,s,r]=i;break;default:throw new Error("Invalid arguments")}re(e),ye(s),xe(s,{progressive:!0,...r?.hints}),s.register(n=>new y(n));const o=M.get(e);return r?.enableLODsManager!==!1&&o.enable(),o}Le();if(!ot){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:M,configureLoader:xe,getRaycastMesh:j,useRaycastMeshes:ke}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.EXTENSION_NAME=F;exports.LODsManager=M;exports.NEEDLE_progressive=y;exports.VERSION=Oe;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=re;exports.getRaycastMesh=j;exports.patchModelViewer=Le;exports.registerRaycastMesh=Re;exports.setDracoDecoderLocation=Te;exports.setKTX2TranscoderLocation=Pe;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=ke;
9
+ ${e.name}`)}break}}}}else o.texture_lod=0}}class ht{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const be=Symbol("NEEDLE_mesh_lod"),te=Symbol("NEEDLE_texture_lod");let fe=null;function _e(){const i=gt();i&&(i.mapURLs(function(t){return De(),t}),De(),fe?.disconnect(),fe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),fe.observe(document,{childList:!0,subtree:!0}))}function gt(){if(typeof customElements>"u")return null;const i=customElements.get("model-viewer");return i||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),_e()}),null)}function De(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{$e(t)})}const Oe=new WeakSet;let pt=0;function $e(i){if(!i||Oe.has(i))return null;Oe.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++pt+`
10
+ `,i.getAttribute("src"));let t=null,e=null,s=null;for(let r=i;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),n=o.find(u=>u.toString()=="Symbol(renderer)"),l=o.find(u=>u.toString()=="Symbol(scene)"),a=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let r=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=M.get(t,{engine:"model-viewer"});return M.addPlugin(new mt),o.enable(),o.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class mt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[te]==!0)return;e[te]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(o&&s&&e.material){let n=function(a){if(a[te]==!0)return;a[te]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let d=0;d<u.length;d++){const x=u[d],L=a[x];if(L?.isTexture===!0){const _=L.userData?.associations?.textures;if(_==null)continue;const R=s.parser.json.textures[_];if(!R){console.warn("Texture data not found for texture index "+_);continue}if(R?.extensions?.[F]){const D=R.extensions[F];D&&o&&m.registerTexture(o,L,D.lods.length,_,D)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[be]==!0)return;e[be]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const o=e.userData?.gltfExtensions?.[F];if(o&&r){const n=e.uuid;m.registerMesh(r,n,e,0,o.lods.length,o)}}}function Be(...i){let t,e,s,r;switch(i.length){case 2:[s,e]=i,r={};break;case 3:[s,e,r]=i;break;case 4:[t,e,s,r]=i;break;default:throw new Error("Invalid arguments")}re(e),ye(s),xe(s,{progressive:!0,...r?.hints}),s.register(n=>new m(n));const o=M.get(e);return r?.enableLODsManager!==!1&&o.enable(),o}_e();if(!ot){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:M,configureLoader:xe,getRaycastMesh:j,useRaycastMeshes:ke}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.EXTENSION_NAME=F;exports.LODsManager=M;exports.NEEDLE_progressive=m;exports.VERSION=Se;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=re;exports.getRaycastMesh=j;exports.patchModelViewer=_e;exports.registerRaycastMesh=Re;exports.setDracoDecoderLocation=Pe;exports.setKTX2TranscoderLocation=Ce;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=ke;
@@ -76,6 +76,12 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
76
76
  static assignTextureLOD(materialOrTexture: Material, level: number): Promise<Array<ProgressiveMaterialTextureLoadingResult> | null>;
77
77
  static assignTextureLOD(materialOrTexture: Mesh, level: number): Promise<Array<ProgressiveMaterialTextureLoadingResult> | null>;
78
78
  static assignTextureLOD(materialOrTexture: Texture, level: number): Promise<Texture | null>;
79
+ /**
80
+ * 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.
81
+ * @default 50 on desktop, 20 on mobile devices
82
+ */
83
+ static set maxConcurrentLoadingTasks(value: number);
84
+ static get maxConcurrentLoadingTasks(): number;
79
85
  private static assignTextureLODForSlot;
80
86
  private readonly parser;
81
87
  private readonly url;
@@ -128,7 +134,6 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
128
134
  private static readonly workers;
129
135
  private static _workersIndex;
130
136
  private static getOrLoadLOD;
131
- private static maxConcurrent;
132
137
  private static queue;
133
138
  private static assignLODInformation;
134
139
  private static getAssignedLODInformation;
package/lib/extension.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BufferGeometry, Mesh, Texture, TextureLoader } from "three";
2
2
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { addDracoAndKTX2Loaders } from "./loaders.js";
4
- import { determineTextureMemoryInBytes, getParam, PromiseQueue, resolveUrl } from "./utils.internal.js";
4
+ import { determineTextureMemoryInBytes, getParam, isMobileDevice, PromiseQueue, resolveUrl } from "./utils.internal.js";
5
5
  import { getRaycastMesh, registerRaycastMesh } from "./utils.js";
6
6
  // All of this has to be removed
7
7
  // import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
@@ -310,6 +310,16 @@ export class NEEDLE_progressive {
310
310
  }
311
311
  return Promise.resolve(null);
312
312
  }
313
+ /**
314
+ * 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.
315
+ * @default 50 on desktop, 20 on mobile devices
316
+ */
317
+ static set maxConcurrentLoadingTasks(value) {
318
+ NEEDLE_progressive.queue.maxConcurrent = value;
319
+ }
320
+ static get maxConcurrentLoadingTasks() {
321
+ return NEEDLE_progressive.queue.maxConcurrent;
322
+ }
313
323
  // #region INTERNAL
314
324
  static assignTextureLODForSlot(current, level, material, slot) {
315
325
  if (current?.isTexture !== true) {
@@ -1046,8 +1056,7 @@ export class NEEDLE_progressive {
1046
1056
  }
1047
1057
  return null;
1048
1058
  }
1049
- static maxConcurrent = 50;
1050
- static queue = new PromiseQueue(NEEDLE_progressive.maxConcurrent, { debug: debug != false });
1059
+ static queue = new PromiseQueue(isMobileDevice() ? 20 : 50, { debug: debug != false });
1051
1060
  static assignLODInformation(url, res, key, level, index) {
1052
1061
  if (!res)
1053
1062
  return;
@@ -19,11 +19,11 @@ export type SlotReturnValue<T = any> = {
19
19
  * Use the `slot` method to request a slot for a promise with a specific key. The returned promise resolves to an object with a `use` method that can be called to add the promise to the queue.
20
20
  */
21
21
  export declare class PromiseQueue<T = any> {
22
- readonly maxConcurrent: number;
22
+ maxConcurrent: number;
23
23
  private readonly _running;
24
24
  private readonly _queue;
25
25
  debug: boolean;
26
- constructor(maxConcurrent?: number, opts?: {
26
+ constructor(maxConcurrent: number, opts?: {
27
27
  debug?: boolean;
28
28
  });
29
29
  private tick;
@@ -71,7 +71,7 @@ export class PromiseQueue {
71
71
  _running = new Map();
72
72
  _queue = [];
73
73
  debug = false;
74
- constructor(maxConcurrent = 100, opts = {}) {
74
+ constructor(maxConcurrent, opts = {}) {
75
75
  this.maxConcurrent = maxConcurrent;
76
76
  this.debug = opts.debug ?? false;
77
77
  window.requestAnimationFrame(this.tick);
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // replaced at build time
2
- export const version = "3.4.0-beta.1";
2
+ export const version = "3.4.0-beta.3";
3
3
  globalThis["GLTF_PROGRESSIVE_VERSION"] = version;
4
4
  console.debug(`[gltf-progressive] version ${version || "-"}`);
@@ -21,7 +21,7 @@ export function getWorker(opts) {
21
21
  class GLTFLoaderWorker {
22
22
  worker;
23
23
  static async createWorker(opts) {
24
- const worker = new Worker(new URL(`./loader.worker.js`, import.meta.url), {
24
+ const worker = new Worker(new URL(`./gltf-progressive.worker.js`, import.meta.url), {
25
25
  type: 'module',
26
26
  });
27
27
  const instance = new GLTFLoaderWorker(worker, opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-progressive",
3
- "version": "3.4.0-beta.1",
3
+ "version": "3.4.0-beta.3",
4
4
  "description": "three.js support for loading glTF or GLB files that contain progressive loading data",
5
5
  "homepage": "https://needle.tools",
6
6
  "author": {