@needle-tools/gltf-progressive 3.3.0-next.3a5f32f → 3.3.1

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.3.1] - 2025-08-18
8
+ - Add: `waitForFirstCapture` option to LodsManager (e.g. `lodsManager.awaitLoading({waitForFirstCapture: true})`) to be able to not resolve the promise until at any promise has been capture (e.g. any LOD has started and finished loading). If necessary a signal can be provided to timeout.
9
+
7
10
  ## [3.3.0] - 2025-08-15
8
11
  - Add LODsManager `overrideLodLevel` property. When set to a number >= 0 texture and mesh LODs will be loaded for that level
9
12
 
package/README.md CHANGED
@@ -141,7 +141,6 @@ Create a new class extending `NEEDLE_progressive_plugin` and add your plugin by
141
141
  Call `lodsManager.awaitLoading(<opts?>)` to receive a promise that will resolve when all object LODs that start loading during the next frame have finished to update. Use the optional options parameter to e.g. wait for more frames.
142
142
 
143
143
  ### Global LOD level override
144
- Set the static `LODsManager.overrideGlobalLodLevel = <level>` to any number between 0 and 6. To disable the override again set it to `undefined`.
145
144
 
146
145
  ### LOD Manager settings
147
146
  These settings are available on the LOD manager instance:
@@ -150,6 +149,7 @@ These settings are available on the LOD manager instance:
150
149
  - `updateInterval` - The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 2, the LODs will be updated every second frame, etc.
151
150
  - `pause` - If set to true, the LODsManager will not update the LODs.
152
151
  - `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.
152
+ - `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`.
153
153
 
154
154
  ### Automatically use low-poly meshes for raycasting
155
155
  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.
@@ -1,4 +1,4 @@
1
- import { BufferGeometry as Q, Mesh as q, Box3 as he, Vector3 as A, Sphere as De, CompressedTexture as $e, Texture as E, Matrix3 as Ge, InterleavedBuffer as Fe, InterleavedBufferAttribute as Ue, BufferAttribute as We, TextureLoader as ze, Matrix4 as Le, Clock as Ee, MeshStandardMaterial as Ne } from "three";
1
+ import { BufferGeometry as Q, Mesh as q, Box3 as he, Vector3 as A, Sphere as De, CompressedTexture as $e, Texture as E, Matrix3 as Ge, InterleavedBuffer as Fe, InterleavedBufferAttribute as Ue, BufferAttribute as We, TextureLoader as ze, Matrix4 as _e, Clock as Ee, MeshStandardMaterial as Ne } from "three";
2
2
  import { GLTFLoader as ye } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { MeshoptDecoder as qe } from "three/examples/jsm/libs/meshopt_decoder.module.js";
4
4
  import { DRACOLoader as Ve } from "three/examples/jsm/loaders/DRACOLoader.js";
@@ -6,8 +6,8 @@ import { KTX2Loader as Xe } from "three/examples/jsm/loaders/KTX2Loader.js";
6
6
  const Ke = "";
7
7
  globalThis.GLTF_PROGRESSIVE_VERSION = Ke;
8
8
  console.debug("[gltf-progressive] version -");
9
- let C = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", V = "https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";
10
- const je = C, Ye = V, Oe = new URL(C + "draco_decoder.js");
9
+ let k = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", V = "https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";
10
+ const je = k, Ye = V, Oe = new URL(k + "draco_decoder.js");
11
11
  Oe.searchParams.append("range", "true");
12
12
  fetch(Oe, {
13
13
  method: "GET",
@@ -15,16 +15,16 @@ fetch(Oe, {
15
15
  Range: "bytes=0-1"
16
16
  }
17
17
  }).catch((i) => {
18
- console.debug(`Failed to fetch remote Draco decoder from ${C} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), C === je && Qe("./include/draco/"), V === Ye && Je("./include/ktx2/");
18
+ console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), k === je && Qe("./include/draco/"), V === Ye && Je("./include/ktx2/");
19
19
  }).finally(() => {
20
20
  Pe();
21
21
  });
22
22
  const He = () => ({
23
- dracoDecoderPath: C,
23
+ dracoDecoderPath: k,
24
24
  ktx2TranscoderPath: V
25
25
  });
26
26
  function Qe(i) {
27
- C = i, T && T[ge] != C ? (console.debug("Updating Draco decoder path to " + i), T[ge] = C, T.setDecoderPath(C), T.preload()) : console.debug("Setting Draco decoder path to " + i);
27
+ k = i, T && T[ge] != k ? (console.debug("Updating Draco decoder path to " + i), T[ge] = k, T.setDecoderPath(k), T.preload()) : console.debug("Setting Draco decoder path to " + i);
28
28
  }
29
29
  function Je(i) {
30
30
  V = i, R && R.transcoderPath != V ? (console.debug("Updating KTX2 transcoder path to " + i), R.setTranscoderPath(V), R.init()) : console.debug("Setting KTX2 transcoder path to " + i);
@@ -38,7 +38,7 @@ function Se(i) {
38
38
  const ge = Symbol("dracoDecoderPath");
39
39
  let T, ne, R;
40
40
  function Pe() {
41
- T || (T = new Ve(), T[ge] = C, T.setDecoderPath(C), T.setDecoderConfig({ type: "js" }), T.preload()), R || (R = new Xe(), R.setTranscoderPath(V), R.init()), ne || (ne = qe);
41
+ T || (T = new Ve(), T[ge] = k, T.setDecoderPath(k), T.setDecoderConfig({ type: "js" }), T.preload()), R || (R = new Xe(), R.setTranscoderPath(V), R.init()), ne || (ne = qe);
42
42
  }
43
43
  const pe = /* @__PURE__ */ new WeakMap();
44
44
  function Te(i, t) {
@@ -78,7 +78,7 @@ let j;
78
78
  function Ae() {
79
79
  return j !== void 0 || (j = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), X("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", j)), j;
80
80
  }
81
- function _e() {
81
+ function Le() {
82
82
  if (typeof window > "u") return !1;
83
83
  const i = new URL(window.location.href), t = i.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);
84
84
  return i.hostname === "127.0.0.1" || t;
@@ -367,8 +367,8 @@ class y {
367
367
  if (c && c.lods) {
368
368
  a.min_count = Math.min(a.min_count, c.lods.length), a.max_count = Math.max(a.max_count, c.lods.length);
369
369
  for (let p = 0; p < c.lods.length; p++) {
370
- const L = c.lods[p];
371
- L.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, L.height), a.lods[p].max_height = Math.max(a.lods[p].max_height, L.height));
370
+ const _ = c.lods[p];
371
+ _.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, _.height), a.lods[p].max_height = Math.max(a.lods[p].max_height, _.height));
372
372
  }
373
373
  }
374
374
  }
@@ -633,10 +633,10 @@ class y {
633
633
  if (u.endsWith(".glb") || u.endsWith(".gltf")) {
634
634
  if (!o.guid)
635
635
  return console.warn("missing pointer for glb/gltf texture", o), null;
636
- const c = u + "_" + o.guid, p = await this.queue.slot(u), L = this.previouslyLoaded.get(c);
637
- if (L !== void 0) {
636
+ const c = u + "_" + o.guid, p = await this.queue.slot(u), _ = this.previouslyLoaded.get(c);
637
+ if (_ !== void 0) {
638
638
  s && console.log(`LOD ${e} was already loading/loaded: ${c}`);
639
- let d = await L.catch((O) => (console.error(`Error loading LOD ${e} from ${u}
639
+ let d = await _.catch((O) => (console.error(`Error loading LOD ${e} from ${u}
640
640
  `, O), null)), x = !1;
641
641
  if (d == null || (d instanceof E && t instanceof E ? d.image?.data || d.source?.data ? d = this.copySettings(t, d) : (x = !0, this.previouslyLoaded.delete(c)) : d instanceof Q && t instanceof Q && (d.attributes.position?.array || (x = !0, this.previouslyLoaded.delete(c)))), !x)
642
642
  return d;
@@ -769,23 +769,26 @@ class ue {
769
769
  r.add(t, e, s);
770
770
  });
771
771
  };
772
- frame_start;
773
- frame_capture_end;
774
772
  ready;
775
- _resolve;
776
- _signal;
777
- /**
778
- * The number of promises that have been added to this group so far.
779
- */
773
+ /** The number of promises that have been added to this group so far */
780
774
  get awaitedCount() {
781
775
  return this._addedCount;
782
776
  }
777
+ /** The number of promises that have been resolved */
783
778
  get resolvedCount() {
784
779
  return this._resolvedCount;
785
780
  }
781
+ /** The number of promises that are in-flight */
786
782
  get currentlyAwaiting() {
787
783
  return this._awaiting.length;
788
784
  }
785
+ _resolve;
786
+ _signal;
787
+ /** start frame can be undefined if the user configured this group to wait for the first promise.
788
+ * Then the start frame will be set when the first promise has been added to the group */
789
+ _frame_start;
790
+ /** How many frames to capture since the start frame */
791
+ _frames_to_capture;
789
792
  _resolved = !1;
790
793
  _addedCount = 0;
791
794
  _resolvedCount = 0;
@@ -794,7 +797,7 @@ class ue {
794
797
  _maxPromisesPerObject = 1;
795
798
  constructor(t, e) {
796
799
  const n = Math.max(e.frames ?? 2, 2);
797
- this.frame_start = t, this.frame_capture_end = t + n, this.ready = new Promise((r) => {
800
+ this._frame_start = e.waitForFirstCapture ? void 0 : t, this._frames_to_capture = n, this.ready = new Promise((r) => {
798
801
  this._resolve = r;
799
802
  }), this.ready.finally(() => {
800
803
  this._resolved = !0, this._awaiting.length = 0;
@@ -804,7 +807,7 @@ class ue {
804
807
  }
805
808
  _currentFrame = 0;
806
809
  update(t) {
807
- this._currentFrame = t, (this._signal?.aborted || this._currentFrame > this.frame_capture_end && this._awaiting.length === 0) && this.resolveNow();
810
+ 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();
808
811
  }
809
812
  _seen = /* @__PURE__ */ new WeakMap();
810
813
  add(t, e, s) {
@@ -812,7 +815,7 @@ class ue {
812
815
  g && console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
813
816
  return;
814
817
  }
815
- if (!(this._currentFrame > this.frame_capture_end)) {
818
+ if (!(this._frame_start !== void 0 && this._currentFrame > this._frame_start + this._frames_to_capture)) {
816
819
  if (this._maxPromisesPerObject >= 1)
817
820
  if (this._seen.has(e)) {
818
821
  let n = this._seen.get(e);
@@ -836,8 +839,8 @@ class ue {
836
839
  });
837
840
  }
838
841
  }
839
- const k = X("debugprogressive"), gt = X("noprogressive"), de = Symbol("Needle:LODSManager"), ce = Symbol("Needle:LODState"), U = Symbol("Needle:CurrentLOD"), P = { mesh_lod: -1, texture_lod: -1 };
840
- class _ {
842
+ const C = X("debugprogressive"), gt = X("noprogressive"), de = Symbol("Needle:LODSManager"), ce = Symbol("Needle:LODState"), U = Symbol("Needle:CurrentLOD"), P = { mesh_lod: -1, texture_lod: -1 };
843
+ class L {
841
844
  /**
842
845
  * Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
843
846
  */
@@ -861,7 +864,7 @@ class _ {
861
864
  static get(t, e) {
862
865
  if (t[de])
863
866
  return console.debug("[gltf-progressive] LODsManager already exists for this renderer"), t[de];
864
- const s = new _(t, {
867
+ const s = new L(t, {
865
868
  engine: "unknown",
866
869
  ...e
867
870
  });
@@ -869,7 +872,7 @@ class _ {
869
872
  }
870
873
  renderer;
871
874
  context;
872
- projectionScreenMatrix = new Le();
875
+ projectionScreenMatrix = new _e();
873
876
  /** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
874
877
  get plugins() {
875
878
  return W;
@@ -918,7 +921,7 @@ class _ {
918
921
  const n = performance.now();
919
922
  return s.ready.finally(() => {
920
923
  const r = this._newPromiseGroups.indexOf(s);
921
- r >= 0 && (this._newPromiseGroups.splice(r, 1), _e() && performance.measure("LODsManager:awaitLoading", {
924
+ r >= 0 && (this._newPromiseGroups.splice(r, 1), Le() && performance.measure("LODsManager:awaitLoading", {
922
925
  start: n,
923
926
  detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
924
927
  }));
@@ -961,7 +964,7 @@ class _ {
961
964
  const e = this;
962
965
  xe(this.renderer), this.renderer.render = function(s, n) {
963
966
  const r = e.renderer.getRenderTarget();
964
- (r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), 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));
967
+ (r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length, C && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
965
968
  const o = t++;
966
969
  e.#t.call(this, s, n), e.onAfterRender(s, n, o);
967
970
  };
@@ -981,7 +984,7 @@ class _ {
981
984
  (l.name === "EffectMaterial" || l.name === "CopyShader") && (o = !1);
982
985
  }
983
986
  if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (o = !1), o) {
984
- if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, k && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, k && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
987
+ if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, C && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, C && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
985
988
  return;
986
989
  this.internalUpdate(t, e), this._postprocessPromiseGroups();
987
990
  }
@@ -995,7 +998,7 @@ class _ {
995
998
  const r = this.targetTriangleDensity;
996
999
  for (const a of n) {
997
1000
  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")) {
998
- 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)));
1001
+ C && (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)));
999
1002
  continue;
1000
1003
  }
1001
1004
  switch (a.material.type) {
@@ -1007,7 +1010,7 @@ class _ {
1007
1010
  case "MeshDepthMaterial":
1008
1011
  continue;
1009
1012
  }
1010
- if (k === "color" && a.material && !a.object.progressive_debug_color) {
1013
+ if (C === "color" && a.material && !a.object.progressive_debug_color) {
1011
1014
  a.object.progressive_debug_color = !0;
1012
1015
  const c = Math.random() * 16777215, p = new Ne({ color: c });
1013
1016
  a.object.material = p;
@@ -1082,7 +1085,7 @@ class _ {
1082
1085
  _sphere = new De();
1083
1086
  _tempBox = new he();
1084
1087
  _tempBox2 = new he();
1085
- tempMatrix = new Le();
1088
+ tempMatrix = new _e();
1086
1089
  _tempWorldPosition = new A();
1087
1090
  _tempBoxSize = new A();
1088
1091
  _tempBox2Size = new A();
@@ -1108,9 +1111,9 @@ class _ {
1108
1111
  return;
1109
1112
  }
1110
1113
  let l = 10 + 1, a = !1;
1111
- if (k && e["DEBUG:LOD"] != null)
1114
+ if (C && e["DEBUG:LOD"] != null)
1112
1115
  return e["DEBUG:LOD"];
1113
- const u = y.getMeshLODExtension(e.geometry)?.lods, c = y.getPrimitiveIndex(e.geometry), p = u && u.length > 0, L = y.getMaterialMinMaxLODsCount(e.material), v = L.min_count !== 1 / 0 && L.min_count >= 0 && L.max_count >= 0;
1116
+ const u = y.getMeshLODExtension(e.geometry)?.lods, c = y.getPrimitiveIndex(e.geometry), p = u && u.length > 0, _ = y.getMaterialMinMaxLODsCount(e.material), v = _.min_count !== 1 / 0 && _.min_count >= 0 && _.max_count >= 0;
1114
1117
  if (!p && !v) {
1115
1118
  r.mesh_lod = 0, r.texture_lod = 0;
1116
1119
  return;
@@ -1123,11 +1126,11 @@ class _ {
1123
1126
  if (!d.boundingBox)
1124
1127
  d.computeBoundingBox();
1125
1128
  else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
1126
- if (!d[_.$skinnedMeshBoundsOffset]) {
1127
- const O = _.skinnedMeshBoundsFrameOffsetCounter++;
1128
- d[_.$skinnedMeshBoundsOffset] = O;
1129
+ if (!d[L.$skinnedMeshBoundsOffset]) {
1130
+ const O = L.skinnedMeshBoundsFrameOffsetCounter++;
1131
+ d[L.$skinnedMeshBoundsOffset] = O;
1129
1132
  }
1130
- const x = d[_.$skinnedMeshBoundsOffset];
1133
+ const x = d[L.$skinnedMeshBoundsOffset];
1131
1134
  if ((s.frames + x) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
1132
1135
  const O = J(d), B = d.geometry;
1133
1136
  O && (d.geometry = O), d.computeBoundingBox(), d.geometry = B;
@@ -1145,7 +1148,7 @@ class _ {
1145
1148
  return;
1146
1149
  }
1147
1150
  }
1148
- if (this._tempBox.copy(G), this._tempBox.applyMatrix4(e.matrixWorld), d.isPerspectiveCamera && _.isInside(this._tempBox, this.projectionScreenMatrix)) {
1151
+ if (this._tempBox.copy(G), this._tempBox.applyMatrix4(e.matrixWorld), d.isPerspectiveCamera && L.isInside(this._tempBox, this.projectionScreenMatrix)) {
1149
1152
  r.mesh_lod = 0, r.texture_lod = 0;
1150
1153
  return;
1151
1154
  }
@@ -1163,35 +1166,35 @@ class _ {
1163
1166
  const O = t.matrixWorldInverse, B = this._tempBox2;
1164
1167
  B.copy(G), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(O);
1165
1168
  const b = B.getSize(this._tempBox2Size), z = Math.max(b.x, b.y);
1166
- if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.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, k && _.debugDrawLine) {
1169
+ if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.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, C && L.debugDrawLine) {
1167
1170
  const f = this.tempMatrix.copy(this.projectionScreenMatrix);
1168
1171
  f.invert();
1169
- const h = _.corner0, M = _.corner1, D = _.corner2, $ = _.corner3;
1172
+ const h = L.corner0, M = L.corner1, D = L.corner2, $ = L.corner3;
1170
1173
  h.copy(this._tempBox.min), M.copy(this._tempBox.max), M.x = h.x, D.copy(this._tempBox.max), D.y = h.y, $.copy(this._tempBox.max);
1171
1174
  const K = (h.z + $.z) * 0.5;
1172
- h.z = M.z = D.z = $.z = K, h.applyMatrix4(f), M.applyMatrix4(f), D.applyMatrix4(f), $.applyMatrix4(f), _.debugDrawLine(h, M, 255), _.debugDrawLine(h, D, 255), _.debugDrawLine(M, $, 255), _.debugDrawLine(D, $, 255);
1175
+ h.z = M.z = D.z = $.z = K, h.applyMatrix4(f), M.applyMatrix4(f), D.applyMatrix4(f), $.applyMatrix4(f), L.debugDrawLine(h, M, 255), L.debugDrawLine(h, D, 255), L.debugDrawLine(M, $, 255), L.debugDrawLine(D, $, 255);
1173
1176
  }
1174
1177
  let w = 999;
1175
1178
  if (u && s.lastScreenCoverage > 0)
1176
1179
  for (let f = 0; f < u.length; f++) {
1177
1180
  const h = u[f], D = (h.densities?.[c] || h.density || 1e-5) / s.lastScreenCoverage;
1178
- if (c > 0 && _e() && !h.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.")), D < n) {
1181
+ if (c > 0 && Le() && !h.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.")), D < n) {
1179
1182
  w = f;
1180
1183
  break;
1181
1184
  }
1182
1185
  }
1183
1186
  w < l && (l = w, a = !0);
1184
1187
  }
1185
- if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh, k && r.mesh_lod != s.lastLodLevel_Mesh) {
1188
+ if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh, C && r.mesh_lod != s.lastLodLevel_Mesh) {
1186
1189
  const x = u?.[r.mesh_lod];
1187
1190
  x && console.debug(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${x.densities?.[c].toFixed(0)}) | ${e.name}`);
1188
1191
  }
1189
1192
  if (v) {
1190
1193
  const d = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
1191
1194
  if (s.lastLodLevel_Texture < 0) {
1192
- if (r.texture_lod = L.max_count - 1, k) {
1193
- const x = L.lods[L.max_count - 1];
1194
- k && console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`);
1195
+ if (r.texture_lod = _.max_count - 1, C) {
1196
+ const x = _.lods[_.max_count - 1];
1197
+ C && console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`);
1195
1198
  }
1196
1199
  } else {
1197
1200
  const x = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
@@ -1199,10 +1202,10 @@ class _ {
1199
1202
  this.context?.engine === "model-viewer" && (O *= 1.5);
1200
1203
  const b = I / window.devicePixelRatio * O;
1201
1204
  let z = !1;
1202
- for (let S = L.lods.length - 1; S >= 0; S--) {
1203
- const w = L.lods[S];
1205
+ for (let S = _.lods.length - 1; S >= 0; S--) {
1206
+ const w = _.lods[S];
1204
1207
  if (!(d && w.max_height >= 2048) && !(Ae() && w.max_height > 4096) && (w.max_height > b || !z && S === 0)) {
1205
- if (z = !0, r.texture_lod = S, k && r.texture_lod < s.lastLodLevel_Texture) {
1208
+ if (z = !0, r.texture_lod = S, C && r.texture_lod < s.lastLodLevel_Texture) {
1206
1209
  const m = w.max_height;
1207
1210
  console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${m}px
1208
1211
  Screensize: ${b.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
@@ -1226,14 +1229,14 @@ class pt {
1226
1229
  }
1227
1230
  const ve = Symbol("NEEDLE_mesh_lod"), se = Symbol("NEEDLE_texture_lod");
1228
1231
  let fe = null;
1229
- function ke() {
1232
+ function Ce() {
1230
1233
  const i = mt();
1231
1234
  i && (i.mapURLs(function(t) {
1232
1235
  return be(), t;
1233
1236
  }), be(), fe?.disconnect(), fe = new MutationObserver((t) => {
1234
1237
  t.forEach((e) => {
1235
1238
  e.addedNodes.forEach((s) => {
1236
- s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ce(s);
1239
+ s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && ke(s);
1237
1240
  });
1238
1241
  });
1239
1242
  }), fe.observe(document, { childList: !0, subtree: !0 }));
@@ -1242,18 +1245,18 @@ function mt() {
1242
1245
  if (typeof customElements > "u") return null;
1243
1246
  const i = customElements.get("model-viewer");
1244
1247
  return i || (customElements.whenDefined("model-viewer").then(() => {
1245
- console.debug("[gltf-progressive] model-viewer defined"), ke();
1248
+ console.debug("[gltf-progressive] model-viewer defined"), Ce();
1246
1249
  }), null);
1247
1250
  }
1248
1251
  function be() {
1249
1252
  if (typeof document > "u") return;
1250
1253
  document.querySelectorAll("model-viewer").forEach((t) => {
1251
- Ce(t);
1254
+ ke(t);
1252
1255
  });
1253
1256
  }
1254
1257
  const Me = /* @__PURE__ */ new WeakSet();
1255
1258
  let yt = 0;
1256
- function Ce(i) {
1259
+ function ke(i) {
1257
1260
  if (!i || Me.has(i))
1258
1261
  return null;
1259
1262
  Me.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++yt + `
@@ -1276,8 +1279,8 @@ function Ce(i) {
1276
1279
  }
1277
1280
  };
1278
1281
  console.debug("[gltf-progressive] setup model-viewer");
1279
- const r = _.get(t, { engine: "model-viewer" });
1280
- return _.addPlugin(new xt()), r.enable(), r.addEventListener("changed", () => {
1282
+ const r = L.get(t, { engine: "model-viewer" });
1283
+ return L.addPlugin(new xt()), r.enable(), r.addEventListener("changed", () => {
1281
1284
  s?.call(i);
1282
1285
  }), i.addEventListener("model-visibility", (o) => {
1283
1286
  o.detail.visible && s?.call(i);
@@ -1316,9 +1319,9 @@ class xt {
1316
1319
  a[se] = !0, a.userData && (a.userData.LOD = -1);
1317
1320
  const u = Object.keys(a);
1318
1321
  for (let c = 0; c < u.length; c++) {
1319
- const p = u[c], L = a[p];
1320
- if (L?.isTexture === !0) {
1321
- const v = L.userData?.associations?.textures;
1322
+ const p = u[c], _ = a[p];
1323
+ if (_?.isTexture === !0) {
1324
+ const v = _.userData?.associations?.textures;
1322
1325
  if (v == null) continue;
1323
1326
  const I = s.parser.json.textures[v];
1324
1327
  if (!I) {
@@ -1327,7 +1330,7 @@ class xt {
1327
1330
  }
1328
1331
  if (I?.extensions?.[F]) {
1329
1332
  const G = I.extensions[F];
1330
- G && r && y.registerTexture(r, L, G.lods.length, v, G);
1333
+ G && r && y.registerTexture(r, _, G.lods.length, v, G);
1331
1334
  }
1332
1335
  }
1333
1336
  }
@@ -1369,15 +1372,15 @@ function wt(...i) {
1369
1372
  progressive: !0,
1370
1373
  ...n?.hints
1371
1374
  }), s.register((o) => new y(o));
1372
- const r = _.get(e);
1375
+ const r = L.get(e);
1373
1376
  return n?.enableLODsManager !== !1 && r.enable(), r;
1374
1377
  }
1375
- ke();
1378
+ Ce();
1376
1379
  if (!rt) {
1377
1380
  const i = {
1378
1381
  gltfProgressive: {
1379
1382
  useNeedleProgressive: wt,
1380
- LODsManager: _,
1383
+ LODsManager: L,
1381
1384
  configureLoader: Te,
1382
1385
  getRaycastMesh: J,
1383
1386
  useRaycastMeshes: ot
@@ -1391,14 +1394,14 @@ if (!rt) {
1391
1394
  }
1392
1395
  export {
1393
1396
  F as EXTENSION_NAME,
1394
- _ as LODsManager,
1397
+ L as LODsManager,
1395
1398
  y as NEEDLE_progressive,
1396
1399
  Ke as VERSION,
1397
1400
  Se as addDracoAndKTX2Loaders,
1398
1401
  Te as configureLoader,
1399
1402
  xe as createLoaders,
1400
1403
  J as getRaycastMesh,
1401
- ke as patchModelViewer,
1404
+ Ce as patchModelViewer,
1402
1405
  nt as registerRaycastMesh,
1403
1406
  Qe as setDracoDecoderLocation,
1404
1407
  Je as setKTX2TranscoderLocation,
@@ -1,8 +1,8 @@
1
- import{BufferGeometry as V,Mesh as N,Box3 as ne,Vector3 as I,Sphere as ve,CompressedTexture as Ne,Texture as j,Matrix3 as je,InterleavedBuffer as Fe,InterleavedBufferAttribute as Ue,BufferAttribute as We,TextureLoader as ze,Matrix4 as Le,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ie}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}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/",F="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ye=k,Qe=F,De=new URL(k+"draco_decoder.js");De.searchParams.append("range","true"),fetch(De,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ye&&Me("./include/draco/"),F===Qe&&_e("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:k,ktx2TranscoderPath:F});function Me(i){k=i,P&&P[le]!=k?(console.debug("Updating Draco decoder path to "+i),P[le]=k,P.setDecoderPath(k),P.preload()):console.debug("Setting Draco decoder path to "+i)}function _e(i){F=i,C&&C.transcoderPath!=F?(console.debug("Updating KTX2 transcoder path to "+i),C.setTranscoderPath(F),C.init()):console.debug("Setting KTX2 transcoder path to "+i)}function J(i){return Oe(),i?C.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:Z}}function ae(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(C),i.meshoptDecoder||i.setMeshoptDecoder(Z)}const le=Symbol("dracoDecoderPath");let P,Z,C;function Oe(){P||(P=new Ke,P[le]=k,P.setDecoderPath(k),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new He,C.setTranscoderPath(F),C.init()),Z||(Z=Xe)}const ue=new WeakMap;function ce(i,t){let e=ue.get(i);e?e=Object.assign(e,t):e=t,ue.set(i,e)}const Ze=ie.prototype.load;function et(...i){const t=ue.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Ze?.call(this,...i)}ie.prototype.load=et,U("debugprogressive");function U(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 tt(i,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||i===void 0)return t;const e=i.lastIndexOf("/");if(e>=0){const r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let ee;function Se(){return ee!==void 0||(ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),U("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee)),ee}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:r,resolve:o}=this._queue.shift();o({use:s=>this.add(r,s)})}}}const st=typeof window>"u"&&typeof document>"u",de=Symbol("needle:raycast-mesh");function z(i){return i?.[de]instanceof V?i[de]:null}function Te(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!z(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[de]=e}}function Ae(i=!0){if(i){if(X)return;const t=X=N.prototype.raycast;N.prototype.raycast=function(e,r){const o=this,s=z(o);let n;s&&o.isMesh&&(n=o.geometry,o.geometry=s),t.call(this,e,r),n&&(o.geometry=n)}}else{if(!X)return;N.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new V;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const R=new Array,h=U("debugprogressive");let te,W=-1;if(h){let i=function(){W+=1,W>=t&&(W=-1),console.log(`Toggle LOD level [${W}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(W=r,console.log(`Set LOD level to [${W}]`))})}function Ie(i){if(h)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=te===!0)}const K=new Array;let nt=0;const it=Se()?2:10;function at(i){if(K.length<it){const e=K.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ye.createWorker(i||{});return K.push(r),r}const t=nt++%K.length;return K[t]}class ye{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const o=r.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const s of this._running)if(s.url===o.result.url){lt(o.result),s.resolve(o.result);const n=s.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CwaLRBn2.js",import.meta.url),{type:"module"});return new ye(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const s=J(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:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};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 lt(i){for(const t of i.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const o=e.index;r.setIndex(he(o))}for(const o in e.attributes){const s=e.attributes[o],n=he(s);r.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const s=e.morphAttributes[o].map(n=>he(n));r.morphAttributes[o]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new I(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new I(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ve(new I(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)r.addGroup(o.start,o.count,o.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const o=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new Ne(o,s,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new j(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new je(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return i}function he(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,o=new Fe(r,e.stride);t=new Ue(o,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new We(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=U("gltf-progressive-worker"),ct=U("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class g{get name(){return $}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 r=this,o="LODS:minmax",s=t[o];if(s!=null)return s;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=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let w=0;w<f.lods.length;w++){const v=f.lods[w];v.width&&(a.lods[w]=a.lods[w]||{min_height:1/0,max_height:0},a.lods[w].min_height=Math.min(a.lods[w].min_height,v.height),a.lods[w].max_height=Math.max(a.lods[w].max_height,v.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,o;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(o=this.lodInfos.get(s.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 N||t.isMesh===!0){const r=t.geometry,o=this.getAssignedLODInformation(r);if(!o)return Promise.resolve(null);for(const s of R)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,g.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const n=o.index||0;s=s[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:h&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),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 r=t;if(Array.isArray(r.material)){const o=new Array;for(const s of r.material){const n=this.assignTextureLOD(s,e);o.push(n)}return Promise.all(o).then(s=>{const n=new Array;for(const l of s)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,o=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const n=r;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&n.uniforms[l].value!=f&&(n.uniforms[l].value=f,n.uniformsNeedUpdate=!0),f));o.push(u),s.push(l)}}}else for(const n of Object.keys(r)){const l=r[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,n);o.push(a),s.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof j||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):g.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(s?.isTexture===!0){if(s!=t&&r&&o){const n=r[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,r,n,s),null}if(ct&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&h&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[o]=s}return s}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&g.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let s=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(s=!0,g.registerTexture(this.url,n,o.lods?.length,r,o));s||this.parser.getDependency("texture",r).then(n=>{n&&g.registerTexture(this.url,n,o.lods?.length,r,o)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const n=this.parser.associations.get(s);n?.meshes===r&&g.registerMesh(this.url,o.guid,s,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,r,o,s)=>{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(`> Progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${s.lods[0]?.width}x${s.lods[0]?.height}, uuid: ${e.uuid}`,s,e)}e.source&&(e.source[ge]=s);const n=s.guid;g.assignLODInformation(t,e,n,r,o),g.lodInfos.set(n,s),g.lowresCache.set(n,e)};static registerMesh=(t,e,r,o,s,n)=>{const l=r.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},n,r),g.assignLODInformation(t,l,e,o,s),g.lodInfos.set(e,n);let a=g.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],g.lowresCache.set(e,a),o>0&&!z(r)&&Te(r,l);for(const u of R)u.onRegisteredNewMesh?.(r,n)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=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 s=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(n=l.source[ge])}if(n||(n=g.lodInfos.get(s)),n){if(e>0){let u=!1;const f=Array.isArray(n.lods);if(f&&e>=n.lods.length?u=!0:f||(u=!0),u)return this.lowresCache.get(s)}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=tt(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,f=await this.queue.slot(a),w=this.previouslyLoaded.get(u);if(w!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let p=await w.catch(y=>(console.error(`Error loading LOD ${e} from ${a}
2
- `,y),null)),m=!1;if(p==null||(p instanceof j&&t instanceof j?p.image?.data||p.source?.data?p=this.copySettings(t,p):(m=!0,this.previouslyLoaded.delete(u)):p instanceof V&&t instanceof V&&(p.attributes.position?.array||(m=!0,this.previouslyLoaded.delete(u)))),!m)return p}if(!f.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const v=n,T=new Promise(async(p,m)=>{if(ut){const x=await(await at({})).load(a);if(x.textures.length>0)for(const c of x.textures){let d=c.texture;return g.assignLODInformation(o.url,d,s,e,void 0),t instanceof j&&(d=this.copySettings(t,d)),d&&(d.guid=v.guid),p(d)}if(x.geometries.length>0){const c=new Array;for(const d of x.geometries){const b=d.geometry;g.assignLODInformation(o.url,b,s,e,d.primitiveIndex),c.push(b)}return p(c)}return p(null)}const y=new ie;ae(y),h&&(await new Promise(x=>setTimeout(x,1e3)),r&&console.warn("Start loading (delayed) "+a,v.guid));let A=a;if(v&&Array.isArray(v.lods)){const x=v.lods[e];x.hash&&(A+="?v="+x.hash)}const D=await y.loadAsync(A).catch(x=>(console.error(`Error loading LOD ${e} from ${a}
3
- `,x),p(null)));if(!D)return p(null);const E=D.parser;r&&console.log("Loading finished "+a,v.guid);let M=0;if(D.parser.json.textures){let x=!1;for(const c of D.parser.json.textures){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===v.guid){x=!0;break}}M++}if(x){let c=await E.getDependency("texture",M);return c&&g.assignLODInformation(o.url,c,s,e,void 0),r&&console.log('change "'+t.name+'" \u2192 "'+c.name+'"',a,M,c,u),t instanceof j&&(c=this.copySettings(t,c)),c&&(c.guid=v.guid),p(c)}else h&&console.warn("Could not find texture with guid",v.guid,D.parser.json)}if(M=0,D.parser.json.meshes){let x=!1;for(const c of D.parser.json.meshes){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===v.guid){x=!0;break}}M++}if(x){const c=await E.getDependency("mesh",M);if(r&&console.log(`Loaded Mesh "${c.name}"`,a,M,c,u),c.isMesh===!0){const d=c.geometry;return g.assignLODInformation(o.url,d,s,e,0),p(d)}else{const d=new Array;for(let b=0;b<c.children.length;b++){const O=c.children[b];if(O.isMesh===!0){const S=O.geometry;g.assignLODInformation(o.url,S,s,e,b),d.push(S)}}return p(d)}}else h&&console.warn("Could not find mesh with guid",v.guid,D.parser.json)}return p(null)});return this.previouslyLoaded.set(u,T),f.use(T),await T}else if(t instanceof j){r&&console.log("Load texture from uri: "+a);const u=await new ze().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,r&&console.log(n,u)):h&&console.warn("failed loading",a),u}}else h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new rt(g.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,r,o,s){if(!e)return;e.userData||(e.userData={});const n=new dt(t,r,o,s);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 V,Mesh as N,Box3 as ne,Vector3 as I,Sphere as ve,CompressedTexture as Ne,Texture as j,Matrix3 as je,InterleavedBuffer as Fe,InterleavedBufferAttribute as Ue,BufferAttribute as We,TextureLoader as ze,Matrix4 as _e,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ie}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const Le="";globalThis.GLTF_PROGRESSIVE_VERSION=Le,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",F="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ye=k,Qe=F,be=new URL(k+"draco_decoder.js");be.searchParams.append("range","true"),fetch(be,{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===Ye&&De("./include/draco/"),F===Qe&&Me("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:k,ktx2TranscoderPath:F});function De(i){k=i,P&&P[le]!=k?(console.debug("Updating Draco decoder path to "+i),P[le]=k,P.setDecoderPath(k),P.preload()):console.debug("Setting Draco decoder path to "+i)}function Me(i){F=i,C&&C.transcoderPath!=F?(console.debug("Updating KTX2 transcoder path to "+i),C.setTranscoderPath(F),C.init()):console.debug("Setting KTX2 transcoder path to "+i)}function J(i){return Oe(),i?C.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:Z}}function ae(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(C),i.meshoptDecoder||i.setMeshoptDecoder(Z)}const le=Symbol("dracoDecoderPath");let P,Z,C;function Oe(){P||(P=new Ke,P[le]=k,P.setDecoderPath(k),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new He,C.setTranscoderPath(F),C.init()),Z||(Z=Xe)}const ue=new WeakMap;function ce(i,t){let e=ue.get(i);e?e=Object.assign(e,t):e=t,ue.set(i,e)}const Ze=ie.prototype.load;function et(...i){const t=ue.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Ze?.call(this,...i)}ie.prototype.load=et,U("debugprogressive");function U(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 tt(i,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||i===void 0)return t;const e=i.lastIndexOf("/");if(e>=0){const r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let ee;function Se(){return ee!==void 0||(ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),U("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee)),ee}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:r,resolve:o}=this._queue.shift();o({use:s=>this.add(r,s)})}}}const st=typeof window>"u"&&typeof document>"u",de=Symbol("needle:raycast-mesh");function z(i){return i?.[de]instanceof V?i[de]:null}function Te(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!z(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[de]=e}}function Ae(i=!0){if(i){if(X)return;const t=X=N.prototype.raycast;N.prototype.raycast=function(e,r){const o=this,s=z(o);let n;s&&o.isMesh&&(n=o.geometry,o.geometry=s),t.call(this,e,r),n&&(o.geometry=n)}}else{if(!X)return;N.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new V;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const R=new Array,h=U("debugprogressive");let te,W=-1;if(h){let i=function(){W+=1,W>=t&&(W=-1),console.log(`Toggle LOD level [${W}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(W=r,console.log(`Set LOD level to [${W}]`))})}function Ie(i){if(h)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=te===!0)}const K=new Array;let nt=0;const it=Se()?2:10;function at(i){if(K.length<it){const e=K.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ye.createWorker(i||{});return K.push(r),r}const t=nt++%K.length;return K[t]}class ye{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const o=r.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const s of this._running)if(s.url===o.result.url){lt(o.result),s.resolve(o.result);const n=s.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CwaLRBn2.js",import.meta.url),{type:"module"});return new ye(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const s=J(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:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};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 lt(i){for(const t of i.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const o=e.index;r.setIndex(he(o))}for(const o in e.attributes){const s=e.attributes[o],n=he(s);r.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const s=e.morphAttributes[o].map(n=>he(n));r.morphAttributes[o]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new I(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new I(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ve(new I(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)r.addGroup(o.start,o.count,o.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const o=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new Ne(o,s,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new j(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new je(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return i}function he(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,o=new Fe(r,e.stride);t=new Ue(o,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new We(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=U("gltf-progressive-worker"),ct=U("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class g{get name(){return $}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 r=this,o="LODS:minmax",s=t[o];if(s!=null)return s;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=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let w=0;w<f.lods.length;w++){const v=f.lods[w];v.width&&(a.lods[w]=a.lods[w]||{min_height:1/0,max_height:0},a.lods[w].min_height=Math.min(a.lods[w].min_height,v.height),a.lods[w].max_height=Math.max(a.lods[w].max_height,v.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,o;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(o=this.lodInfos.get(s.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 N||t.isMesh===!0){const r=t.geometry,o=this.getAssignedLODInformation(r);if(!o)return Promise.resolve(null);for(const s of R)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,g.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const n=o.index||0;s=s[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:h&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),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 r=t;if(Array.isArray(r.material)){const o=new Array;for(const s of r.material){const n=this.assignTextureLOD(s,e);o.push(n)}return Promise.all(o).then(s=>{const n=new Array;for(const l of s)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,o=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const n=r;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&n.uniforms[l].value!=f&&(n.uniforms[l].value=f,n.uniformsNeedUpdate=!0),f));o.push(u),s.push(l)}}}else for(const n of Object.keys(r)){const l=r[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,n);o.push(a),s.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof j||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):g.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(s?.isTexture===!0){if(s!=t&&r&&o){const n=r[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,r,n,s),null}if(ct&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&h&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[o]=s}return s}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&g.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let s=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(s=!0,g.registerTexture(this.url,n,o.lods?.length,r,o));s||this.parser.getDependency("texture",r).then(n=>{n&&g.registerTexture(this.url,n,o.lods?.length,r,o)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const n=this.parser.associations.get(s);n?.meshes===r&&g.registerMesh(this.url,o.guid,s,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,r,o,s)=>{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(`> Progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${s.lods[0]?.width}x${s.lods[0]?.height}, uuid: ${e.uuid}`,s,e)}e.source&&(e.source[ge]=s);const n=s.guid;g.assignLODInformation(t,e,n,r,o),g.lodInfos.set(n,s),g.lowresCache.set(n,e)};static registerMesh=(t,e,r,o,s,n)=>{const l=r.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},n,r),g.assignLODInformation(t,l,e,o,s),g.lodInfos.set(e,n);let a=g.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],g.lowresCache.set(e,a),o>0&&!z(r)&&Te(r,l);for(const u of R)u.onRegisteredNewMesh?.(r,n)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=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 s=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(n=l.source[ge])}if(n||(n=g.lodInfos.get(s)),n){if(e>0){let u=!1;const f=Array.isArray(n.lods);if(f&&e>=n.lods.length?u=!0:f||(u=!0),u)return this.lowresCache.get(s)}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=tt(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,f=await this.queue.slot(a),w=this.previouslyLoaded.get(u);if(w!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let p=await w.catch(y=>(console.error(`Error loading LOD ${e} from ${a}
2
+ `,y),null)),m=!1;if(p==null||(p instanceof j&&t instanceof j?p.image?.data||p.source?.data?p=this.copySettings(t,p):(m=!0,this.previouslyLoaded.delete(u)):p instanceof V&&t instanceof V&&(p.attributes.position?.array||(m=!0,this.previouslyLoaded.delete(u)))),!m)return p}if(!f.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const v=n,T=new Promise(async(p,m)=>{if(ut){const x=await(await at({})).load(a);if(x.textures.length>0)for(const c of x.textures){let d=c.texture;return g.assignLODInformation(o.url,d,s,e,void 0),t instanceof j&&(d=this.copySettings(t,d)),d&&(d.guid=v.guid),p(d)}if(x.geometries.length>0){const c=new Array;for(const d of x.geometries){const L=d.geometry;g.assignLODInformation(o.url,L,s,e,d.primitiveIndex),c.push(L)}return p(c)}return p(null)}const y=new ie;ae(y),h&&(await new Promise(x=>setTimeout(x,1e3)),r&&console.warn("Start loading (delayed) "+a,v.guid));let A=a;if(v&&Array.isArray(v.lods)){const x=v.lods[e];x.hash&&(A+="?v="+x.hash)}const b=await y.loadAsync(A).catch(x=>(console.error(`Error loading LOD ${e} from ${a}
3
+ `,x),p(null)));if(!b)return p(null);const E=b.parser;r&&console.log("Loading finished "+a,v.guid);let D=0;if(b.parser.json.textures){let x=!1;for(const c of b.parser.json.textures){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===v.guid){x=!0;break}}D++}if(x){let c=await E.getDependency("texture",D);return c&&g.assignLODInformation(o.url,c,s,e,void 0),r&&console.log('change "'+t.name+'" \u2192 "'+c.name+'"',a,D,c,u),t instanceof j&&(c=this.copySettings(t,c)),c&&(c.guid=v.guid),p(c)}else h&&console.warn("Could not find texture with guid",v.guid,b.parser.json)}if(D=0,b.parser.json.meshes){let x=!1;for(const c of b.parser.json.meshes){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===v.guid){x=!0;break}}D++}if(x){const c=await E.getDependency("mesh",D);if(r&&console.log(`Loaded Mesh "${c.name}"`,a,D,c,u),c.isMesh===!0){const d=c.geometry;return g.assignLODInformation(o.url,d,s,e,0),p(d)}else{const d=new Array;for(let L=0;L<c.children.length;L++){const O=c.children[L];if(O.isMesh===!0){const S=O.geometry;g.assignLODInformation(o.url,S,s,e,L),d.push(S)}}return p(d)}}else h&&console.warn("Could not find mesh with guid",v.guid,b.parser.json)}return p(null)});return this.previouslyLoaded.set(u,T),f.use(T),await T}else if(t instanceof j){r&&console.log("Load texture from uri: "+a);const u=await new ze().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,r&&console.log(n,u)):h&&console.warn("failed loading",a),u}}else h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new rt(g.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,r,o,s){if(!e)return;e.userData||(e.userData={});const n=new dt(t,r,o,s);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
4
4
  `,t.uuid,`
5
- `,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,r,o){this.url=t,this.key=e,this.level=r,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,r,o)=>{o.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){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(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const B=U("debugprogressive"),ht=U("noprogressive"),me=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),G=Symbol("Needle:CurrentLOD"),_={mesh_lod:-1,texture_lod:-1};class L{static debugDrawLine;static getObjectLODState(t){return t[pe]}static addPlugin(t){R.push(t)}static removePlugin(t){const e=R.indexOf(t);e>=0&&R.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const r=new L(t,{engine:"unknown",...e});return t[me]=r,r}renderer;context;projectionScreenMatrix=new Le;get plugins(){return R}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new fe(this.#s,{...t});this._newPromiseGroups.push(r);const o=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new qe;#s=0;#o=0;#i=0;#r=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;J(this.renderer),this.renderer.render=function(r,o){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,o),e.onAfterRender(r,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,r){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ht||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=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")){B&&(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(B==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,w=new Ve({color:f});a.object.material=w}const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const n=r.transparent;for(const a of n){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,o){r.userData||(r.userData={});let s=r[pe];if(s||(s=new gt,r[pe]=s),s.frames++<2)return;for(const l of R)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:W;n>=0?(_.mesh_lod=n,_.texture_lod=n):(this.calculateLodLevel(e,r,s,o,_),_.mesh_lod=Math.round(_.mesh_lod),_.texture_lod=Math.round(_.texture_lod)),_.mesh_lod>=0&&this.loadProgressiveMeshes(r,_.mesh_lod),r.material&&_.texture_lod>=0&&this.loadProgressiveTextures(r.material,_.texture_lod,n),h&&r.material&&!r.isGizmo&&Ie(r.material);for(const l of R)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,_);s.lastLodLevel_Mesh=_.mesh_lod,s.lastLodLevel_Texture=_.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let o=!1;if((t[G]===void 0||e<t[G])&&(o=!0),r!==void 0&&r>=0&&(o=t[G]!=r,e=r),o){t[G]=e;const s=g.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[G]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(r=t[G]!=o,e=o),r){t[G]=e;const s=t.geometry,n=g.assignMeshLOD(t,e).then(l=>(l&&t[G]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new ve;_tempBox=new ne;_tempBox2=new ne;tempMatrix=new Le;_tempWorldPosition=new I;_tempBoxSize=new I;_tempBox2Size=new I;static corner0=new I;static corner1=new I;static corner2=new I;static corner3=new I;static _tempPtInside=new I;static isInside(t,e){const r=t.min,o=t.max,s=(r.x+o.x)*.5,n=(r.y+o.y)*.5;return this._tempPtInside.set(s,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,o,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let n=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),f=a&&a.length>0,w=g.getMaterialMinMaxLODsCount(e.material),v=w.min_count!==1/0&&w.min_count>=0&&w.max_count>=0;if(!f&&!v){s.mesh_lod=0,s.texture_lod=0;return}f||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let p=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;if(!m.boundingBox)m.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!m[L.$skinnedMeshBoundsOffset]){const A=L.skinnedMeshBoundsFrameOffsetCounter++;m[L.$skinnedMeshBoundsOffset]=A}const y=m[L.$skinnedMeshBoundsOffset];if((r.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const A=z(m),D=m.geometry;A&&(m.geometry=A),m.computeBoundingBox(),m.geometry=D}}p=m.boundingBox}if(p){const m=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 c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(p),this._tempBox.applyMatrix4(e.matrixWorld),m.isPerspectiveCamera&&L.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.isPerspectiveCamera&&m.fov>70){const c=this._tempBox.min,d=this._tempBox.max;let b=c.x,O=c.y,S=d.x,q=d.y;const H=2,se=1.5,Y=(c.x+d.x)*.5,Q=(c.y+d.y)*.5;b=(b-Y)*H+Y,O=(O-Q)*H+Q,S=(S-Y)*H+Y,q=(q-Q)*H+Q;const Re=b<0&&S>0?0:Math.min(Math.abs(c.x),Math.abs(d.x)),Ge=O<0&&q>0?0:Math.min(Math.abs(c.y),Math.abs(d.y)),oe=Math.max(Re,Ge);r.lastCentrality=(se-oe)*(se-oe)*(se-oe)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&T>0&&y.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const A=t.matrixWorldInverse,D=this._tempBox2;D.copy(p),D.applyMatrix4(e.matrixWorld),D.applyMatrix4(A);const E=D.getSize(this._tempBox2Size),M=Math.max(E.x,E.y);if(Math.max(y.x,y.y)!=0&&M!=0&&(y.z=E.z/Math.max(E.x,E.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,B&&L.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const d=L.corner0,b=L.corner1,O=L.corner2,S=L.corner3;d.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=d.x,O.copy(this._tempBox.max),O.y=d.y,S.copy(this._tempBox.max);const q=(d.z+S.z)*.5;d.z=b.z=O.z=S.z=q,d.applyMatrix4(c),b.applyMatrix4(c),O.applyMatrix4(c),S.applyMatrix4(c),L.debugDrawLine(d,b,255),L.debugDrawLine(d,O,255),L.debugDrawLine(b,S,255),L.debugDrawLine(O,S,255)}let x=999;if(a&&r.lastScreenCoverage>0)for(let c=0;c<a.length;c++){const d=a[c],b=(d.densities?.[u]||d.density||1e-5)/r.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.")),b<o){x=c;break}}x<n&&(n=x,l=!0)}if(l?s.mesh_lod=n:s.mesh_lod=r.lastLodLevel_Mesh,B&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=a?.[s.mesh_lod];m&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${m.densities?.[u].toFixed(0)}) | ${e.name}`)}if(v){const m="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=w.max_count-1,B){const y=w.lods[w.max_count-1];B&&console.log(`First Texture LOD ${s.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let A=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(A*=1.5);const D=T/window.devicePixelRatio*A;let E=!1;for(let M=w.lods.length-1;M>=0;M--){const x=w.lods[M];if(!(m&&x.max_height>=2048)&&!(Se()&&x.max_height>4096)&&(x.max_height>D||!E&&M===0)){if(E=!0,s.texture_lod=M,B&&s.texture_lod<r.lastLodLevel_Texture){const c=x.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${c}px
6
- Screensize: ${D.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
5
+ `,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,r,o){this.url=t,this.key=e,this.level=r,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,r,o)=>{o.forEach(s=>{s.add(t,e,r)})};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,r){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(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const B=U("debugprogressive"),ht=U("noprogressive"),me=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),G=Symbol("Needle:CurrentLOD"),M={mesh_lod:-1,texture_lod:-1};class _{static debugDrawLine;static getObjectLODState(t){return t[pe]}static addPlugin(t){R.push(t)}static removePlugin(t){const e=R.indexOf(t);e>=0&&R.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const r=new _(t,{engine:"unknown",...e});return t[me]=r,r}renderer;context;projectionScreenMatrix=new _e;get plugins(){return R}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new fe(this.#s,{...t});this._newPromiseGroups.push(r);const o=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new qe;#s=0;#o=0;#i=0;#r=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;J(this.renderer),this.renderer.render=function(r,o){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,o),e.onAfterRender(r,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,r){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ht||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=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")){B&&(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(B==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,w=new Ve({color:f});a.object.material=w}const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const n=r.transparent;for(const a of n){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,o){r.userData||(r.userData={});let s=r[pe];if(s||(s=new gt,r[pe]=s),s.frames++<2)return;for(const l of R)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:W;n>=0?(M.mesh_lod=n,M.texture_lod=n):(this.calculateLodLevel(e,r,s,o,M),M.mesh_lod=Math.round(M.mesh_lod),M.texture_lod=Math.round(M.texture_lod)),M.mesh_lod>=0&&this.loadProgressiveMeshes(r,M.mesh_lod),r.material&&M.texture_lod>=0&&this.loadProgressiveTextures(r.material,M.texture_lod,n),h&&r.material&&!r.isGizmo&&Ie(r.material);for(const l of R)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,M);s.lastLodLevel_Mesh=M.mesh_lod,s.lastLodLevel_Texture=M.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let o=!1;if((t[G]===void 0||e<t[G])&&(o=!0),r!==void 0&&r>=0&&(o=t[G]!=r,e=r),o){t[G]=e;const s=g.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[G]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(r=t[G]!=o,e=o),r){t[G]=e;const s=t.geometry,n=g.assignMeshLOD(t,e).then(l=>(l&&t[G]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new ve;_tempBox=new ne;_tempBox2=new ne;tempMatrix=new _e;_tempWorldPosition=new I;_tempBoxSize=new I;_tempBox2Size=new I;static corner0=new I;static corner1=new I;static corner2=new I;static corner3=new I;static _tempPtInside=new I;static isInside(t,e){const r=t.min,o=t.max,s=(r.x+o.x)*.5,n=(r.y+o.y)*.5;return this._tempPtInside.set(s,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,o,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let n=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),f=a&&a.length>0,w=g.getMaterialMinMaxLODsCount(e.material),v=w.min_count!==1/0&&w.min_count>=0&&w.max_count>=0;if(!f&&!v){s.mesh_lod=0,s.texture_lod=0;return}f||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let p=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;if(!m.boundingBox)m.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!m[_.$skinnedMeshBoundsOffset]){const A=_.skinnedMeshBoundsFrameOffsetCounter++;m[_.$skinnedMeshBoundsOffset]=A}const y=m[_.$skinnedMeshBoundsOffset];if((r.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const A=z(m),b=m.geometry;A&&(m.geometry=A),m.computeBoundingBox(),m.geometry=b}}p=m.boundingBox}if(p){const m=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 c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(p),this._tempBox.applyMatrix4(e.matrixWorld),m.isPerspectiveCamera&&_.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.isPerspectiveCamera&&m.fov>70){const c=this._tempBox.min,d=this._tempBox.max;let L=c.x,O=c.y,S=d.x,q=d.y;const H=2,se=1.5,Y=(c.x+d.x)*.5,Q=(c.y+d.y)*.5;L=(L-Y)*H+Y,O=(O-Q)*H+Q,S=(S-Y)*H+Y,q=(q-Q)*H+Q;const Re=L<0&&S>0?0:Math.min(Math.abs(c.x),Math.abs(d.x)),Ge=O<0&&q>0?0:Math.min(Math.abs(c.y),Math.abs(d.y)),oe=Math.max(Re,Ge);r.lastCentrality=(se-oe)*(se-oe)*(se-oe)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&T>0&&y.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const A=t.matrixWorldInverse,b=this._tempBox2;b.copy(p),b.applyMatrix4(e.matrixWorld),b.applyMatrix4(A);const E=b.getSize(this._tempBox2Size),D=Math.max(E.x,E.y);if(Math.max(y.x,y.y)!=0&&D!=0&&(y.z=E.z/Math.max(E.x,E.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,B&&_.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const d=_.corner0,L=_.corner1,O=_.corner2,S=_.corner3;d.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=d.x,O.copy(this._tempBox.max),O.y=d.y,S.copy(this._tempBox.max);const q=(d.z+S.z)*.5;d.z=L.z=O.z=S.z=q,d.applyMatrix4(c),L.applyMatrix4(c),O.applyMatrix4(c),S.applyMatrix4(c),_.debugDrawLine(d,L,255),_.debugDrawLine(d,O,255),_.debugDrawLine(L,S,255),_.debugDrawLine(O,S,255)}let x=999;if(a&&r.lastScreenCoverage>0)for(let c=0;c<a.length;c++){const d=a[c],L=(d.densities?.[u]||d.density||1e-5)/r.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.")),L<o){x=c;break}}x<n&&(n=x,l=!0)}if(l?s.mesh_lod=n:s.mesh_lod=r.lastLodLevel_Mesh,B&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=a?.[s.mesh_lod];m&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${m.densities?.[u].toFixed(0)}) | ${e.name}`)}if(v){const m="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=w.max_count-1,B){const y=w.lods[w.max_count-1];B&&console.log(`First Texture LOD ${s.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let A=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(A*=1.5);const b=T/window.devicePixelRatio*A;let E=!1;for(let D=w.lods.length-1;D>=0;D--){const x=w.lods[D];if(!(m&&x.max_height>=2048)&&!(Se()&&x.max_height>4096)&&(x.max_height>b||!E&&D===0)){if(E=!0,s.texture_lod=D,B&&s.texture_lod<r.lastLodLevel_Texture){const c=x.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${c}px
6
+ Screensize: ${b.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
7
7
  ${e.name}`)}break}}}}else s.texture_lod=0}}class gt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new I;lastCentrality=0}const Ee=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let xe=null;function we(){const i=ft();i&&(i.mapURLs(function(t){return ke(),t}),ke(),xe?.disconnect(),xe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Be(r)})})}),xe.observe(document,{childList:!0,subtree:!0}))}function ft(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function ke(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{Be(i)})}const Ce=new WeakSet;let mt=0;function Be(i){if(!i||Ce.has(i))return null;Ce.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++mt+`
8
- `,i.getAttribute("src"));let t=null,e=null,r=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let o=function(){if(r){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=L.get(t,{engine:"model-viewer"});return L.addPlugin(new pt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{s.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,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 r=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),s=this.getUrl(o);if(s&&r&&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 f=0;f<u.length;f++){const w=u[f],v=a[w];if(v?.isTexture===!0){const T=v.userData?.associations?.textures;if(T==null)continue;const p=r.parser.json.textures[T];if(!p){console.warn("Texture data not found for texture index "+T);continue}if(p?.extensions?.[$]){const m=p.extensions[$];m&&s&&g.registerTexture(s,v,m.lods.length,T,m)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ee]==!0)return;e[Ee]=!0;const r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(!o)return;const s=e.userData?.gltfExtensions?.[$];if(s&&o){const n=e.uuid;g.registerMesh(o,n,e,0,s.lods.length,s)}}}function $e(...i){let t,e,r,o;switch(i.length){case 2:[r,e]=i,o={};break;case 3:[r,e,o]=i;break;case 4:[t,e,r,o]=i;break;default:throw new Error("Invalid arguments")}J(e),ae(r),ce(r,{progressive:!0,...o?.hints}),r.register(n=>new g(n));const s=L.get(e);return o?.enableLODsManager!==!1&&s.enable(),s}if(we(),!st){const i={gltfProgressive:{useNeedleProgressive:$e,LODsManager:L,configureLoader:ce,getRaycastMesh:z,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{$ as EXTENSION_NAME,L as LODsManager,g as NEEDLE_progressive,be as VERSION,ae as addDracoAndKTX2Loaders,ce as configureLoader,J as createLoaders,z as getRaycastMesh,we as patchModelViewer,Te as registerRaycastMesh,Me as setDracoDecoderLocation,_e as setKTX2TranscoderLocation,$e as useNeedleProgressive,Ae as useRaycastMeshes};
8
+ `,i.getAttribute("src"));let t=null,e=null,r=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let o=function(){if(r){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=_.get(t,{engine:"model-viewer"});return _.addPlugin(new pt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{s.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,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 r=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),s=this.getUrl(o);if(s&&r&&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 f=0;f<u.length;f++){const w=u[f],v=a[w];if(v?.isTexture===!0){const T=v.userData?.associations?.textures;if(T==null)continue;const p=r.parser.json.textures[T];if(!p){console.warn("Texture data not found for texture index "+T);continue}if(p?.extensions?.[$]){const m=p.extensions[$];m&&s&&g.registerTexture(s,v,m.lods.length,T,m)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ee]==!0)return;e[Ee]=!0;const r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(!o)return;const s=e.userData?.gltfExtensions?.[$];if(s&&o){const n=e.uuid;g.registerMesh(o,n,e,0,s.lods.length,s)}}}function $e(...i){let t,e,r,o;switch(i.length){case 2:[r,e]=i,o={};break;case 3:[r,e,o]=i;break;case 4:[t,e,r,o]=i;break;default:throw new Error("Invalid arguments")}J(e),ae(r),ce(r,{progressive:!0,...o?.hints}),r.register(n=>new g(n));const s=_.get(e);return o?.enableLODsManager!==!1&&s.enable(),s}if(we(),!st){const i={gltfProgressive:{useNeedleProgressive:$e,LODsManager:_,configureLoader:ce,getRaycastMesh:z,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{$ as EXTENSION_NAME,_ as LODsManager,g as NEEDLE_progressive,Le as VERSION,ae as addDracoAndKTX2Loaders,ce as configureLoader,J as createLoaders,z as getRaycastMesh,we as patchModelViewer,Te as registerRaycastMesh,De as setDracoDecoderLocation,Me as setKTX2TranscoderLocation,$e as useNeedleProgressive,Ae as useRaycastMeshes};
@@ -2,7 +2,7 @@
2
2
  `,S),null)),w=!1;if(c==null||(c instanceof d.Texture&&t instanceof d.Texture?c.image?.data||c.source?.data?c=this.copySettings(t,c):(w=!0,this.previouslyLoaded.delete(f)):c instanceof d.BufferGeometry&&t instanceof d.BufferGeometry&&(c.attributes.position?.array||(w=!0,this.previouslyLoaded.delete(f)))),!w)return c}if(!m.use)return p&&console.log(`LOD ${e} was aborted: ${u}`),null;const M=i,I=new Promise(async(c,w)=>{if(at){const y=await(await ot({})).load(u);if(y.textures.length>0)for(const h of y.textures){let g=h.texture;return x.assignLODInformation(n.url,g,s,e,void 0),t instanceof d.Texture&&(g=this.copySettings(t,g)),g&&(g.guid=M.guid),c(g)}if(y.geometries.length>0){const h=new Array;for(const g of y.geometries){const D=g.geometry;x.assignLODInformation(n.url,D,s,e,g.primitiveIndex),h.push(D)}return c(h)}return c(null)}const S=new pe.GLTFLoader;me(S),p&&(await new Promise(L=>setTimeout(L,1e3)),r&&console.warn("Start loading (delayed) "+u,M.guid));let B=u;if(M&&Array.isArray(M.lods)){const L=M.lods[e];L.hash&&(B+="?v="+L.hash)}const b=await S.loadAsync(B).catch(L=>(console.error(`Error loading LOD ${e} from ${u}
3
3
  `,L),c(null)));if(!b)return c(null);const W=b.parser;r&&console.log("Loading finished "+u,M.guid);let T=0;if(b.parser.json.textures){let L=!1;for(const y of b.parser.json.textures){if(y?.extensions){const h=y?.extensions[G];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){let y=await W.getDependency("texture",T);return y&&x.assignLODInformation(n.url,y,s,e,void 0),r&&console.log('change "'+t.name+'" → "'+y.name+'"',u,T,y,f),t instanceof d.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=M.guid),c(y)}else p&&console.warn("Could not find texture with guid",M.guid,b.parser.json)}if(T=0,b.parser.json.meshes){let L=!1;for(const y of b.parser.json.meshes){if(y?.extensions){const h=y?.extensions[G];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){const y=await W.getDependency("mesh",T);if(r&&console.log(`Loaded Mesh "${y.name}"`,u,T,y,f),y.isMesh===!0){const h=y.geometry;return x.assignLODInformation(n.url,h,s,e,0),c(h)}else{const h=new Array;for(let g=0;g<y.children.length;g++){const D=y.children[g];if(D.isMesh===!0){const O=D.geometry;x.assignLODInformation(n.url,O,s,e,g),h.push(O)}}return c(h)}}else p&&console.warn("Could not find mesh with guid",M.guid,b.parser.json)}return c(null)});return this.previouslyLoaded.set(f,I),m.use(I),await I}else if(t instanceof d.Texture){r&&console.log("Load texture from uri: "+u);const m=await new d.TextureLoader().loadAsync(u);return m?(m.guid=i.guid,m.flipY=!1,m.needsUpdate=!0,m.colorSpace=t.colorSpace,r&&console.log(i,m)):p&&console.warn("failed loading",u),m}}else p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new et(x.maxConcurrent,{debug:p!=!1});static assignLODInformation(t,e,r,n,s){if(!e)return;e.userData||(e.userData={});const i=new ut(t,r,n,s);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}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?(p==="verbose"&&console.debug(`Copy texture settings
4
4
  `,t.uuid,`
5
- `,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 ut{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class le{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const n=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+n,this.ready=new Promise(s=>{this._resolve=s}),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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){p&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){p&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,n+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const C=V("debugprogressive"),dt=V("noprogressive"),ue=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};class _{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){E.push(t)}static removePlugin(t){const e=E.indexOf(t);e>=0&&E.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const r=new _(t,{engine:"unknown",...e});return t[ue]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return E}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(this.#s,{...t});this._newPromiseGroups.push(r);const n=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),_e()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new d.Clock;#s=0;#n=0;#i=0;#r=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(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,C&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,i)}}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,r){if(this.pause)return;const s=this.renderer.renderLists.get(t,0).opaque;let i=!0;if(s.length===1){const l=s[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(dt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,C&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,C&&console.warn("↑ Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),n=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of n){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")){C&&(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(C==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,m=new d.MeshStandardMaterial({color:f});a.object.material=m}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const i=r.transparent;for(const a of i){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[de];if(s||(s=new ct,r[de]=s),s.frames++<2)return;for(const l of E)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(P.mesh_lod=i,P.texture_lod=i):(this.calculateLodLevel(e,r,s,n,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod),r.material&&P.texture_lod>=0&&this.loadProgressiveTextures(r.material,P.texture_lod,i),p&&r.material&&!r.isGizmo&&ke(r.material);for(const l of E)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lastLodLevel_Texture=P.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let n=!1;if((t[U]===void 0||e<t[U])&&(n=!0),r!==void 0&&r>=0&&(n=t[U]!=r,e=r),n){t[U]=e;const s=x.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[U]!=n,e=n),r){t[U]=e;const s=t.geometry,i=x.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,i=(r.y+n.y)*.5;return this._tempPtInside.set(s,i,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,n,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let l=10+1,a=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=x.getMeshLODExtension(e.geometry)?.lods,f=x.getPrimitiveIndex(e.geometry),m=u&&u.length>0,v=x.getMaterialMinMaxLODsCount(e.material),M=v.min_count!==1/0&&v.min_count>=0&&v.max_count>=0;if(!m&&!M){s.mesh_lod=0,s.texture_lod=0;return}m||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let F=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[_.$skinnedMeshBoundsOffset]){const S=_.skinnedMeshBoundsFrameOffsetCounter++;c[_.$skinnedMeshBoundsOffset]=S}const w=c[_.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const S=q(c),B=c.geometry;S&&(c.geometry=S),c.computeBoundingBox(),c.geometry=B}}F=c.boundingBox}if(F){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 h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(F),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&_.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const h=this._tempBox.min,g=this._tempBox.max;let D=h.x,O=h.y,$=g.x,j=g.y;const H=2,se=1.5,Q=(h.x+g.x)*.5,J=(h.y+g.y)*.5;D=(D-Q)*H+Q,O=(O-J)*H+J,$=($-Q)*H+Q,j=(j-J)*H+J;const $e=D<0&&$>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),Ge=O<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ne=Math.max($e,Ge);r.lastCentrality=(se-ne)*(se-ne)*(se-ne)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&I>0&&w.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const S=t.matrixWorldInverse,B=this._tempBox2;B.copy(F),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(S);const b=B.getSize(this._tempBox2Size),W=Math.max(b.x,b.y);if(Math.max(w.x,w.y)!=0&&W!=0&&(w.z=b.z/Math.max(b.x,b.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,C&&_.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=_.corner0,D=_.corner1,O=_.corner2,$=_.corner3;g.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=g.x,O.copy(this._tempBox.max),O.y=g.y,$.copy(this._tempBox.max);const j=(g.z+$.z)*.5;g.z=D.z=O.z=$.z=j,g.applyMatrix4(h),D.applyMatrix4(h),O.applyMatrix4(h),$.applyMatrix4(h),_.debugDrawLine(g,D,255),_.debugDrawLine(g,O,255),_.debugDrawLine(D,$,255),_.debugDrawLine(O,$,255)}let L=999;if(u&&r.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const g=u[h],O=(g.densities?.[f]||g.density||1e-5)/r.lastScreenCoverage;if(f>0&&_e()&&!g.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.")),O<n){L=h;break}}L<l&&(l=L,a=!0)}if(a?s.mesh_lod=l:s.mesh_lod=r.lastLodLevel_Mesh,C&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${s.mesh_lod} (density: ${w.densities?.[f].toFixed(0)}) | ${e.name}`)}if(M){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=v.max_count-1,C){const w=v.lods[v.max_count-1];C&&console.log(`First Texture LOD ${s.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let S=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(S*=1.5);const b=I/window.devicePixelRatio*S;let W=!1;for(let T=v.lods.length-1;T>=0;T--){const L=v.lods[T];if(!(c&&L.max_height>=2048)&&!(Ae()&&L.max_height>4096)&&(L.max_height>b||!W&&T===0)){if(W=!0,s.texture_lod=T,C&&s.texture_lod<r.lastLodLevel_Texture){const y=L.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${y}px
5
+ `,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 ut{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class le{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.add(t,e,r)})};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 n=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=n,this.ready=new Promise(s=>{this._resolve=s}),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,r){if(this._resolved){p&&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 n=this._seen.get(e);if(n>=this._maxPromisesPerObject){p&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,n+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const C=V("debugprogressive"),dt=V("noprogressive"),ue=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};class _{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){E.push(t)}static removePlugin(t){const e=E.indexOf(t);e>=0&&E.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const r=new _(t,{engine:"unknown",...e});return t[ue]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return E}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(this.#s,{...t});this._newPromiseGroups.push(r);const n=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),_e()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new d.Clock;#s=0;#n=0;#i=0;#r=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(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,C&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,i)}}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,r){if(this.pause)return;const s=this.renderer.renderLists.get(t,0).opaque;let i=!0;if(s.length===1){const l=s[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(dt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,C&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,C&&console.warn("↑ Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),n=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of n){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")){C&&(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(C==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,m=new d.MeshStandardMaterial({color:f});a.object.material=m}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const i=r.transparent;for(const a of i){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[de];if(s||(s=new ct,r[de]=s),s.frames++<2)return;for(const l of E)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(P.mesh_lod=i,P.texture_lod=i):(this.calculateLodLevel(e,r,s,n,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod),r.material&&P.texture_lod>=0&&this.loadProgressiveTextures(r.material,P.texture_lod,i),p&&r.material&&!r.isGizmo&&ke(r.material);for(const l of E)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lastLodLevel_Texture=P.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let n=!1;if((t[U]===void 0||e<t[U])&&(n=!0),r!==void 0&&r>=0&&(n=t[U]!=r,e=r),n){t[U]=e;const s=x.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[U]!=n,e=n),r){t[U]=e;const s=t.geometry,i=x.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,i=(r.y+n.y)*.5;return this._tempPtInside.set(s,i,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,n,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let l=10+1,a=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=x.getMeshLODExtension(e.geometry)?.lods,f=x.getPrimitiveIndex(e.geometry),m=u&&u.length>0,v=x.getMaterialMinMaxLODsCount(e.material),M=v.min_count!==1/0&&v.min_count>=0&&v.max_count>=0;if(!m&&!M){s.mesh_lod=0,s.texture_lod=0;return}m||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let F=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[_.$skinnedMeshBoundsOffset]){const S=_.skinnedMeshBoundsFrameOffsetCounter++;c[_.$skinnedMeshBoundsOffset]=S}const w=c[_.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const S=q(c),B=c.geometry;S&&(c.geometry=S),c.computeBoundingBox(),c.geometry=B}}F=c.boundingBox}if(F){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 h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(F),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&_.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const h=this._tempBox.min,g=this._tempBox.max;let D=h.x,O=h.y,$=g.x,j=g.y;const H=2,se=1.5,Q=(h.x+g.x)*.5,J=(h.y+g.y)*.5;D=(D-Q)*H+Q,O=(O-J)*H+J,$=($-Q)*H+Q,j=(j-J)*H+J;const $e=D<0&&$>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),Ge=O<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ne=Math.max($e,Ge);r.lastCentrality=(se-ne)*(se-ne)*(se-ne)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&I>0&&w.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const S=t.matrixWorldInverse,B=this._tempBox2;B.copy(F),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(S);const b=B.getSize(this._tempBox2Size),W=Math.max(b.x,b.y);if(Math.max(w.x,w.y)!=0&&W!=0&&(w.z=b.z/Math.max(b.x,b.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,C&&_.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=_.corner0,D=_.corner1,O=_.corner2,$=_.corner3;g.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=g.x,O.copy(this._tempBox.max),O.y=g.y,$.copy(this._tempBox.max);const j=(g.z+$.z)*.5;g.z=D.z=O.z=$.z=j,g.applyMatrix4(h),D.applyMatrix4(h),O.applyMatrix4(h),$.applyMatrix4(h),_.debugDrawLine(g,D,255),_.debugDrawLine(g,O,255),_.debugDrawLine(D,$,255),_.debugDrawLine(O,$,255)}let L=999;if(u&&r.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const g=u[h],O=(g.densities?.[f]||g.density||1e-5)/r.lastScreenCoverage;if(f>0&&_e()&&!g.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.")),O<n){L=h;break}}L<l&&(l=L,a=!0)}if(a?s.mesh_lod=l:s.mesh_lod=r.lastLodLevel_Mesh,C&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${s.mesh_lod} (density: ${w.densities?.[f].toFixed(0)}) | ${e.name}`)}if(M){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=v.max_count-1,C){const w=v.lods[v.max_count-1];C&&console.log(`First Texture LOD ${s.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let S=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(S*=1.5);const b=I/window.devicePixelRatio*S;let W=!1;for(let T=v.lods.length-1;T>=0;T--){const L=v.lods[T];if(!(c&&L.max_height>=2048)&&!(Ae()&&L.max_height>4096)&&(L.max_height>b||!W&&T===0)){if(W=!0,s.texture_lod=T,C&&s.texture_lod<r.lastLodLevel_Texture){const y=L.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${y}px
6
6
  Screensize: ${b.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${w.toFixed(1)}
7
7
  ${e.name}`)}break}}}}else s.texture_lod=0}}class ct{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}const ve=Symbol("NEEDLE_mesh_lod"),Z=Symbol("NEEDLE_texture_lod");let ce=null;function we(){const o=ft();o&&(o.mapURLs(function(t){return Me(),t}),Me(),ce?.disconnect(),ce=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Re(r)})})}),ce.observe(document,{childList:!0,subtree:!0}))}function ft(){if(typeof customElements>"u")return null;const o=customElements.get("model-viewer");return o||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function Me(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Re(t)})}const be=new WeakSet;let ht=0;function Re(o){if(!o||be.has(o))return null;be.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++ht+`
8
8
  `,o.getAttribute("src"));let t=null,e=null,r=null;for(let n=o;n!=null;n=Object.getPrototypeOf(n)){const s=Object.getOwnPropertySymbols(n),i=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&l!=null&&(e=o[l]),!r&&a!=null&&(r=o[a])}if(t&&e){let n=function(){if(r){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}r?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=_.get(t,{engine:"model-viewer"});return _.addPlugin(new gt),s.enable(),s.addEventListener("changed",()=>{r?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(o)}),o.addEventListener("load",()=>{n()}),()=>{s.disable()}}return null}class gt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,n){this.tryParseMeshLOD(e,n),this.tryParseTextureLOD(e,n)}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[Z]==!0)return;e[Z]=!0;const r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let i=function(a){if(a[Z]==!0)return;a[Z]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const m=u[f],v=a[m];if(v?.isTexture===!0){const M=v.userData?.associations?.textures;if(M==null)continue;const I=r.parser.json.textures[M];if(!I){console.warn("Texture data not found for texture index "+M);continue}if(I?.extensions?.[G]){const F=I.extensions[G];F&&s&&x.registerTexture(s,v,F.lods.length,M,F)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)i(a);else i(l)}}tryParseMeshLOD(t,e){if(e[ve]==!0)return;e[ve]=!0;const r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(!n)return;const s=e.userData?.gltfExtensions?.[G];if(s&&n){const i=e.uuid;x.registerMesh(n,i,e,0,s.lods.length,s)}}}function Be(...o){let t,e,r,n;switch(o.length){case 2:[r,e]=o,n={};break;case 3:[r,e,n]=o;break;case 4:[t,e,r,n]=o;break;default:throw new Error("Invalid arguments")}re(e),me(r),ye(r,{progressive:!0,...n?.hints}),r.register(i=>new x(i));const s=_.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}we();if(!tt){const o={gltfProgressive:{useNeedleProgressive:Be,LODsManager:_,configureLoader:ye,getRaycastMesh:q,useRaycastMeshes:Ce}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=G;exports.LODsManager=_;exports.NEEDLE_progressive=x;exports.VERSION=De;exports.addDracoAndKTX2Loaders=me;exports.configureLoader=ye;exports.createLoaders=re;exports.getRaycastMesh=q;exports.patchModelViewer=we;exports.registerRaycastMesh=Ie;exports.setDracoDecoderLocation=Se;exports.setKTX2TranscoderLocation=Te;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=Ce;
@@ -2,10 +2,14 @@ type PromiseType = "texture" | "mesh";
2
2
  export type PromiseGroupOptions = {
3
3
  /** Name for debugging purposes */
4
4
  name?: string;
5
- /** Define many frames new LOD promises will be captured and awaited. The group will resolve after all promises captured during this time have resolved (or when the abort signal is triggered).
5
+ /** Define how many frames new LOD promises will at least be captured and awaited. The group will resolve after all promises captured during this time have resolved (or when the abort signal is triggered).
6
6
  * @default 2 frames, which means the group will capture promises for 2 frames before resolving.
7
7
  */
8
8
  frames?: number;
9
+ /** If set to true at least one LOD loading promise must be captured before this promise will resolve.
10
+ * After the first promise has been captured the group will wait for the amount of frames specified in the `frames` option.
11
+ */
12
+ waitForFirstCapture?: boolean;
9
13
  /** An optional signal to abort the promise */
10
14
  signal?: AbortSignal;
11
15
  /**
@@ -34,17 +38,20 @@ type PromiseGroupResolveResult = {
34
38
  */
35
39
  export declare class PromiseGroup {
36
40
  static readonly addPromise: (type: PromiseType, object: object, promise: Promise<any>, groups: PromiseGroup[]) => void;
37
- readonly frame_start: number;
38
- readonly frame_capture_end: number;
39
41
  readonly ready: Promise<PromiseGroupResolveResult>;
40
- private _resolve;
41
- private readonly _signal?;
42
- /**
43
- * The number of promises that have been added to this group so far.
44
- */
42
+ /** The number of promises that have been added to this group so far */
45
43
  get awaitedCount(): number;
44
+ /** The number of promises that have been resolved */
46
45
  get resolvedCount(): number;
46
+ /** The number of promises that are in-flight */
47
47
  get currentlyAwaiting(): number;
48
+ private _resolve;
49
+ private readonly _signal?;
50
+ /** start frame can be undefined if the user configured this group to wait for the first promise.
51
+ * Then the start frame will be set when the first promise has been added to the group */
52
+ private _frame_start;
53
+ /** How many frames to capture since the start frame */
54
+ private _frames_to_capture;
48
55
  private _resolved;
49
56
  private _addedCount;
50
57
  private _resolvedCount;
@@ -9,23 +9,26 @@ export class PromiseGroup {
9
9
  group.add(type, object, promise);
10
10
  });
11
11
  };
12
- frame_start;
13
- frame_capture_end;
14
12
  ready;
15
- _resolve;
16
- _signal;
17
- /**
18
- * The number of promises that have been added to this group so far.
19
- */
13
+ /** The number of promises that have been added to this group so far */
20
14
  get awaitedCount() {
21
15
  return this._addedCount;
22
16
  }
17
+ /** The number of promises that have been resolved */
23
18
  get resolvedCount() {
24
19
  return this._resolvedCount;
25
20
  }
21
+ /** The number of promises that are in-flight */
26
22
  get currentlyAwaiting() {
27
23
  return this._awaiting.length;
28
24
  }
25
+ _resolve;
26
+ _signal;
27
+ /** start frame can be undefined if the user configured this group to wait for the first promise.
28
+ * Then the start frame will be set when the first promise has been added to the group */
29
+ _frame_start;
30
+ /** How many frames to capture since the start frame */
31
+ _frames_to_capture;
29
32
  _resolved = false;
30
33
  _addedCount = 0;
31
34
  _resolvedCount = 0;
@@ -35,8 +38,8 @@ export class PromiseGroup {
35
38
  constructor(frame, options) {
36
39
  const minFrames = 2; // wait at least 2 frames to capture promises
37
40
  const framesToCapture = Math.max(options.frames ?? minFrames, minFrames); // default to 2 frames and make sure it's at least 2 frames
38
- this.frame_start = frame;
39
- this.frame_capture_end = frame + framesToCapture;
41
+ this._frame_start = options.waitForFirstCapture ? undefined : frame;
42
+ this._frames_to_capture = framesToCapture;
40
43
  this.ready = new Promise((resolve) => {
41
44
  this._resolve = resolve;
42
45
  });
@@ -53,8 +56,12 @@ export class PromiseGroup {
53
56
  _currentFrame = 0;
54
57
  update(frame) {
55
58
  this._currentFrame = frame;
59
+ // Check if start frame is not defined yet but we have added objects since the last update
60
+ if (this._frame_start === undefined && this._addedCount > 0) {
61
+ this._frame_start = frame;
62
+ }
56
63
  // If we've passes the frame capture end frame and didn't add any promises, we resolve immediately
57
- if (this._signal?.aborted || (this._currentFrame > this.frame_capture_end && this._awaiting.length === 0)) {
64
+ if (this._signal?.aborted || (this._awaiting.length === 0 && (this._frame_start !== undefined && (frame > this._frame_start + this._frames_to_capture)))) {
58
65
  this.resolveNow();
59
66
  }
60
67
  }
@@ -65,7 +72,7 @@ export class PromiseGroup {
65
72
  console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
66
73
  return;
67
74
  }
68
- if (this._currentFrame > this.frame_capture_end) {
75
+ if (this._frame_start !== undefined && this._currentFrame > this._frame_start + this._frames_to_capture) {
69
76
  return; // we are not capturing any more promises
70
77
  }
71
78
  if (this._maxPromisesPerObject >= 1) {
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // replaced at build time
2
- export const version = "3.3.0";
2
+ export const version = "3.3.1";
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.3.0-next.3a5f32f",
3
+ "version": "3.3.1",
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": {
@@ -72,4 +72,4 @@
72
72
  "vite": "7"
73
73
  },
74
74
  "types": "./lib/index.d.ts"
75
- }
75
+ }