@needle-tools/gltf-progressive 3.4.0-beta.1 → 3.4.0-next.8580e96

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.2] - 2026-03-16
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,6 +1,6 @@
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 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 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 = "";
@@ -38,7 +38,7 @@ function Pe(n) {
38
38
  const pe = /* @__PURE__ */ Symbol("dracoDecoderPath");
39
39
  let C, oe, $;
40
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);
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
44
  function Ce(n, t) {
@@ -84,7 +84,7 @@ function ve() {
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) {
@@ -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));
@@ -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
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,13 +789,13 @@ ${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
  }
@@ -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(50, { debug: f != !1 });
870
879
  static assignLODInformation(t, e, s, r, i) {
871
880
  if (!e) return;
872
881
  e.userData || (e.userData = {});
@@ -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,7 +1216,7 @@ 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);
@@ -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") {
@@ -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
  }
@@ -1480,7 +1489,7 @@ class _t {
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
  }
@@ -1502,7 +1511,7 @@ function Lt(...n) {
1502
1511
  we(e), Pe(s), Ce(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
  }
@@ -1526,7 +1535,7 @@ 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
1540
  Pe as addDracoAndKTX2Loaders,
1532
1541
  Ce as configureLoader,
@@ -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 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,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 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[fe]=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[fe]&&(n=l.source[fe])}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(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 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 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[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=f.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=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 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=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)&&!(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
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 ye=null;function we(){const i=pt();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),ye?.disconnect(),ye=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),ye.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"),we()}),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(we(),!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,be as VERSION,le as addDracoAndKTX2Loaders,de as configureLoader,ee as createLoaders,V as getRaycastMesh,we as patchModelViewer,Ce as registerRaycastMesh,De as setDracoDecoderLocation,Oe 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 _e=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&&_e(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?_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,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=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 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(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),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: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&&ve()&&!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)&&!(Ae()&&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
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;
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[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;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}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=m;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;
@@ -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
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
@@ -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
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(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.2";
3
3
  globalThis["GLTF_PROGRESSIVE_VERSION"] = version;
4
4
  console.debug(`[gltf-progressive] version ${version || "-"}`);
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-next.8580e96",
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": {
@@ -73,4 +73,4 @@
73
73
  "vite": "7"
74
74
  },
75
75
  "types": "./lib/index.d.ts"
76
- }
76
+ }