@needle-tools/gltf-progressive 3.4.0-beta.2 → 3.4.0-canary.4c8f053

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,7 +4,7 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [3.4.0-beta.2] - 2026-03-16
7
+ ## [3.4.0-beta.3] - 2026-03-24
8
8
  - Add: `NEEDLE_progressive.maxConcurrentLoadingTasks` property to control how many LOD meshes and textures are allowed to be fetched concurrently. Defaults to `50`
9
9
 
10
10
  ## [3.3.5] - 2025-10-08
@@ -1,4 +1,4 @@
1
- import { BufferGeometry as E, Mesh as X, Box3 as ge, Vector3 as A, Sphere as Oe, CompressedTexture as Ge, Texture as F, Matrix3 as Fe, InterleavedBuffer as We, InterleavedBufferAttribute as Ue, BufferAttribute as ze, TextureLoader as Ne, Matrix4 as Le, Clock as qe, MeshStandardMaterial as Ve } from "three";
1
+ import { BufferGeometry as E, Mesh as X, Box3 as ge, Vector3 as A, Sphere as Se, CompressedTexture as Ge, Texture as F, Matrix3 as Fe, InterleavedBuffer as We, InterleavedBufferAttribute as Ue, BufferAttribute as ze, TextureLoader as qe, Matrix4 as ve, Clock as Ne, MeshStandardMaterial as Ve } from "three";
2
2
  import { GLTFLoader as xe } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { MeshoptDecoder as Ee } from "three/examples/jsm/libs/meshopt_decoder.module.js";
4
4
  import { DRACOLoader as Xe } from "three/examples/jsm/loaders/DRACOLoader.js";
@@ -7,9 +7,9 @@ const Ke = "";
7
7
  globalThis.GLTF_PROGRESSIVE_VERSION = Ke;
8
8
  console.debug("[gltf-progressive] version -");
9
9
  let I = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", j = "https://cdn.needle.tools/static/three/0.179.1/basis2/";
10
- const Ye = I, He = j, Se = new URL(I + "draco_decoder.js");
11
- Se.searchParams.append("range", "true");
12
- fetch(Se, {
10
+ const Ye = I, He = j, Pe = new URL(I + "draco_decoder.js");
11
+ Pe.searchParams.append("range", "true");
12
+ fetch(Pe, {
13
13
  method: "GET",
14
14
  headers: {
15
15
  Range: "bytes=0-1"
@@ -17,7 +17,7 @@ fetch(Se, {
17
17
  }).catch((n) => {
18
18
  console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), I === Ye && Je("./include/draco/"), j === He && Ze("./include/ktx2/");
19
19
  }).finally(() => {
20
- Te();
20
+ Ce();
21
21
  });
22
22
  const Qe = () => ({
23
23
  dracoDecoderPath: I,
@@ -30,18 +30,18 @@ function Ze(n) {
30
30
  j = n, $ && $.transcoderPath != j ? (console.debug("Updating KTX2 transcoder path to " + n), $.setTranscoderPath(j), $.init()) : console.debug("Setting KTX2 transcoder path to " + n);
31
31
  }
32
32
  function we(n) {
33
- return Te(), n ? $.detectSupport(n) : n !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: C, ktx2Loader: $, meshoptDecoder: oe };
33
+ return Ce(), n ? $.detectSupport(n) : n !== null && console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"), { dracoLoader: C, ktx2Loader: $, meshoptDecoder: oe };
34
34
  }
35
- function Pe(n) {
35
+ function Te(n) {
36
36
  n.dracoLoader || n.setDRACOLoader(C), n.ktx2Loader || n.setKTX2Loader($), n.meshoptDecoder || n.setMeshoptDecoder(oe);
37
37
  }
38
38
  const pe = /* @__PURE__ */ Symbol("dracoDecoderPath");
39
39
  let C, oe, $;
40
- function Te() {
40
+ function Ce() {
41
41
  C || (C = new Xe(), C[pe] = I, C.setDecoderPath(I), C.setDecoderConfig({ type: "js" }), C.preload()), $ || ($ = new je(), $.setTranscoderPath(j), $.init()), oe || (oe = Ee);
42
42
  }
43
43
  const me = /* @__PURE__ */ new WeakMap();
44
- function Ce(n, t) {
44
+ function Ae(n, t) {
45
45
  let e = me.get(n);
46
46
  e ? e = Object.assign(e, t) : e = t, me.set(n, e);
47
47
  }
@@ -57,8 +57,8 @@ function tt(...n) {
57
57
  return n[0] = e, et?.call(this, ...n);
58
58
  }
59
59
  xe.prototype.load = tt;
60
- N("debugprogressive");
61
- function N(n) {
60
+ q("debugprogressive");
61
+ function q(n) {
62
62
  if (typeof window > "u") return !1;
63
63
  const e = new URL(window.location.href).searchParams.get(n);
64
64
  return e == null || e === "0" || e === "false" ? !1 : e === "" ? !0 : e;
@@ -74,18 +74,18 @@ function st(n, t) {
74
74
  }
75
75
  return t;
76
76
  }
77
- function Ae() {
78
- return H !== void 0 || (H = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), N("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", H)), H;
77
+ function _e() {
78
+ return H !== void 0 || (H = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), q("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", H)), H;
79
79
  }
80
80
  let H;
81
- function ve() {
81
+ function be() {
82
82
  if (typeof window > "u") return !1;
83
83
  const n = new URL(window.location.href), t = n.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(n.hostname);
84
84
  return n.hostname === "127.0.0.1" || t;
85
85
  }
86
86
  class rt {
87
87
  constructor(t, e = {}) {
88
- this.maxConcurrent = t, this.debug = e.debug ?? !1, window.requestAnimationFrame(this.tick);
88
+ this.maxConcurrent = t, this.debug = e.debug ?? !1, typeof window < "u" && window.requestAnimationFrame(this.tick);
89
89
  }
90
90
  _running = /* @__PURE__ */ new Map();
91
91
  _queue = [];
@@ -160,7 +160,7 @@ function ut(n) {
160
160
  t.setAttribute(e, n.getAttribute(e));
161
161
  return t.setIndex(n.getIndex()), t;
162
162
  }
163
- const z = new Array(), f = N("debugprogressive");
163
+ const z = new Array(), f = q("debugprogressive");
164
164
  let Z, V = -1;
165
165
  if (f) {
166
166
  let n = function() {
@@ -181,18 +181,18 @@ function ke(n) {
181
181
  }
182
182
  const J = new Array();
183
183
  let ct = 0;
184
- const dt = Ae() ? 2 : 10;
184
+ const dt = _e() ? 2 : 10;
185
185
  function ft(n) {
186
186
  if (J.length < dt) {
187
187
  const s = J.length;
188
188
  f && console.warn(`[Worker] Creating new worker #${s}`);
189
- const r = _e.createWorker(n || {});
189
+ const r = Le.createWorker(n || {});
190
190
  return J.push(r), r;
191
191
  }
192
192
  const t = ct++ % J.length;
193
193
  return J[t];
194
194
  }
195
- class _e {
195
+ class Le {
196
196
  constructor(t, e) {
197
197
  this.worker = t, this._debug = e.debug ?? !1, t.onmessage = (s) => {
198
198
  const r = s.data;
@@ -214,12 +214,12 @@ class _e {
214
214
  static async createWorker(t) {
215
215
  const e = new Worker(new URL(
216
216
  /* @vite-ignore */
217
- "/assets/loader.worker-CCrD-Ycm.js",
217
+ "/assets/gltf-progressive.worker-CCrD-Ycm.js",
218
218
  import.meta.url
219
219
  ), {
220
220
  type: "module"
221
221
  });
222
- return new _e(e, t);
222
+ return new Le(e, t);
223
223
  }
224
224
  _running = [];
225
225
  _webglRenderer = null;
@@ -272,7 +272,7 @@ function ht(n) {
272
272
  e.boundingBox?.max.x,
273
273
  e.boundingBox?.max.y,
274
274
  e.boundingBox?.max.z
275
- ), s.boundingSphere = new Oe(
275
+ ), s.boundingSphere = new Se(
276
276
  new A(
277
277
  e.boundingSphere?.center.x,
278
278
  e.boundingSphere?.center.y,
@@ -332,9 +332,9 @@ function le(n) {
332
332
  } else "isBufferAttribute" in n && n.isBufferAttribute && (t = new ze(n.array, n.itemSize, n.normalized), t.usage = n.usage, t.gpuType = n.gpuType, t.updateRanges = n.updateRanges);
333
333
  return t;
334
334
  }
335
- const gt = N("gltf-progressive-worker");
336
- N("gltf-progressive-reduce-mipmaps");
337
- const K = N("gltf-progressive-gc"), ue = /* @__PURE__ */ Symbol("needle-progressive-texture"), W = "NEEDLE_progressive";
335
+ const gt = q("gltf-progressive-worker");
336
+ q("gltf-progressive-reduce-mipmaps");
337
+ const K = q("gltf-progressive-gc"), ue = /* @__PURE__ */ Symbol("needle-progressive-texture"), W = "NEEDLE_progressive";
338
338
  class m {
339
339
  /** The name of the extension */
340
340
  get name() {
@@ -508,7 +508,7 @@ class m {
508
508
  }
509
509
  /**
510
510
  * Set the maximum number of concurrent loading tasks for LOD resources. This limits how many LOD resources (meshes or textures) can be loaded at the same time to prevent overloading the network or GPU. If the limit is reached, additional loading requests will be queued and processed as previous ones finish.
511
- * @default 50
511
+ * @default 50 on desktop, 20 on mobile devices
512
512
  */
513
513
  static set maxConcurrentLoadingTasks(t) {
514
514
  m.queue.maxConcurrent = t;
@@ -802,7 +802,7 @@ ${e}`);
802
802
  return d(null);
803
803
  }
804
804
  const b = new xe();
805
- Pe(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
805
+ Te(b), f && (await new Promise((L) => setTimeout(L, 1e3)), s && console.warn("Start loading (delayed) " + u, _.guid));
806
806
  let B = u;
807
807
  if (_ && Array.isArray(_.lods)) {
808
808
  const L = _.lods[e];
@@ -812,7 +812,7 @@ ${e}`);
812
812
  `, L), d(null)));
813
813
  if (!D)
814
814
  return d(null);
815
- const q = D.parser;
815
+ const N = D.parser;
816
816
  s && console.log("Loading finished " + u, _.guid);
817
817
  let P = 0;
818
818
  if (D.parser.json.textures) {
@@ -828,7 +828,7 @@ ${e}`);
828
828
  P++;
829
829
  }
830
830
  if (L) {
831
- let x = await q.getDependency("texture", P);
831
+ let x = await N.getDependency("texture", P);
832
832
  return x && m.assignLODInformation(r.url, x, i, e, void 0), s && console.log('change "' + t.name + '" → "' + x.name + '"', u, P, x, c), t instanceof F && (x = this.copySettings(t, x)), x && (x.guid = _.guid), d(x);
833
833
  } else f && console.warn("Could not find texture with guid", _.guid, D.parser.json);
834
834
  }
@@ -845,7 +845,7 @@ ${e}`);
845
845
  P++;
846
846
  }
847
847
  if (L) {
848
- const x = await q.getDependency("mesh", P);
848
+ const x = await N.getDependency("mesh", P);
849
849
  if (s && console.log(`Loaded Mesh "${x.name}"`, u, P, x, c), x.isMesh === !0) {
850
850
  const h = x.geometry;
851
851
  return m.assignLODInformation(r.url, h, i, e, 0), d(h);
@@ -869,13 +869,16 @@ ${e}`);
869
869
  return M != null ? M instanceof F ? (this.cache.set(c, new WeakRef(M)), m._resourceRegistry.register(M, c)) : Array.isArray(M) ? this.cache.set(c, Promise.resolve(M)) : this.cache.set(c, Promise.resolve(M)) : this.cache.set(c, Promise.resolve(null)), M;
870
870
  } else if (t instanceof F) {
871
871
  s && console.log("Load texture from uri: " + u);
872
- const y = await new Ne().loadAsync(u);
872
+ const y = await new qe().loadAsync(u);
873
873
  return y ? (y.guid = o.guid, y.flipY = !1, y.needsUpdate = !0, y.colorSpace = t.colorSpace, s && console.log(o, y)) : f && console.warn("failed loading", u), y;
874
874
  }
875
875
  }
876
876
  return null;
877
877
  }
878
- static queue = new rt(50, { debug: f != !1 });
878
+ static _queue;
879
+ static get queue() {
880
+ return this._queue ??= new rt(_e() ? 20 : 50, { debug: f != !1 });
881
+ }
879
882
  static assignLODInformation(t, e, s, r, i) {
880
883
  if (!e) return;
881
884
  e.userData || (e.userData = {});
@@ -979,7 +982,7 @@ class ce {
979
982
  });
980
983
  }
981
984
  }
982
- const R = N("debugprogressive"), mt = N("noprogressive"), de = /* @__PURE__ */ Symbol("Needle:LODSManager"), fe = /* @__PURE__ */ Symbol("Needle:LODState"), U = /* @__PURE__ */ Symbol("Needle:CurrentLOD"), T = { mesh_lod: -1, texture_lod: -1 };
985
+ const R = q("debugprogressive"), mt = q("noprogressive"), de = /* @__PURE__ */ Symbol("Needle:LODSManager"), fe = /* @__PURE__ */ Symbol("Needle:LODState"), U = /* @__PURE__ */ Symbol("Needle:CurrentLOD"), T = { mesh_lod: -1, texture_lod: -1 };
983
986
  class v {
984
987
  /**
985
988
  * 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.
@@ -1012,7 +1015,7 @@ class v {
1012
1015
  }
1013
1016
  renderer;
1014
1017
  context;
1015
- projectionScreenMatrix = new Le();
1018
+ projectionScreenMatrix = new ve();
1016
1019
  /** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
1017
1020
  get plugins() {
1018
1021
  return z;
@@ -1061,7 +1064,7 @@ class v {
1061
1064
  const r = performance.now();
1062
1065
  return s.ready.finally(() => {
1063
1066
  const i = this._newPromiseGroups.indexOf(s);
1064
- i >= 0 && (this._newPromiseGroups.splice(i, 1), ve() && performance.measure("LODsManager:awaitLoading", {
1067
+ i >= 0 && (this._newPromiseGroups.splice(i, 1), be() && performance.measure("LODsManager:awaitLoading", {
1065
1068
  start: r,
1066
1069
  detail: { id: e, name: t?.name, awaited: s.awaitedCount, resolved: s.resolvedCount }
1067
1070
  }));
@@ -1087,7 +1090,7 @@ class v {
1087
1090
  this.renderer = t, this.context = { ...e };
1088
1091
  }
1089
1092
  #t;
1090
- #o = new qe();
1093
+ #o = new Ne();
1091
1094
  #r = 0;
1092
1095
  #i = 0;
1093
1096
  #n = 0;
@@ -1222,10 +1225,10 @@ class v {
1222
1225
  return Promise.resolve(null);
1223
1226
  }
1224
1227
  // private testIfLODLevelsAreAvailable() {
1225
- _sphere = new Oe();
1228
+ _sphere = new Se();
1226
1229
  _tempBox = new ge();
1227
1230
  _tempBox2 = new ge();
1228
- tempMatrix = new Le();
1231
+ tempMatrix = new ve();
1229
1232
  _tempWorldPosition = new A();
1230
1233
  _tempBoxSize = new A();
1231
1234
  _tempBox2Size = new A();
@@ -1305,8 +1308,8 @@ class v {
1305
1308
  g.multiplyScalar(0.5), screen.availHeight > 0 && k > 0 && g.multiplyScalar(k / screen.availHeight), t.isPerspectiveCamera ? g.x *= t.aspect : t.isOrthographicCamera;
1306
1309
  const b = t.matrixWorldInverse, B = this._tempBox2;
1307
1310
  B.copy(M), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(b);
1308
- const D = B.getSize(this._tempBox2Size), q = Math.max(D.x, D.y);
1309
- if (Math.max(g.x, g.y) != 0 && q != 0 && (g.z = D.z / Math.max(D.x, D.y) * Math.max(g.x, g.y)), s.lastScreenCoverage = Math.max(g.x, g.y, g.z), s.lastScreenspaceVolume.copy(g), s.lastScreenCoverage *= s.lastCentrality, R && v.debugDrawLine) {
1311
+ const D = B.getSize(this._tempBox2Size), N = Math.max(D.x, D.y);
1312
+ if (Math.max(g.x, g.y) != 0 && N != 0 && (g.z = D.z / Math.max(D.x, D.y) * Math.max(g.x, g.y)), s.lastScreenCoverage = Math.max(g.x, g.y, g.z), s.lastScreenspaceVolume.copy(g), s.lastScreenCoverage *= s.lastCentrality, R && v.debugDrawLine) {
1310
1313
  const h = this.tempMatrix.copy(this.projectionScreenMatrix);
1311
1314
  h.invert();
1312
1315
  const p = v.corner0, O = v.corner1, S = v.corner2, G = v.corner3;
@@ -1318,7 +1321,7 @@ class v {
1318
1321
  if (u && s.lastScreenCoverage > 0)
1319
1322
  for (let h = 0; h < u.length; h++) {
1320
1323
  const p = u[h], S = (p.densities?.[c] || p.density || 1e-5) / s.lastScreenCoverage;
1321
- if (c > 0 && ve() && !p.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] && (window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] = !0, console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")), S < r) {
1324
+ if (c > 0 && be() && !p.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] && (window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] = !0, console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")), S < r) {
1322
1325
  L = h;
1323
1326
  break;
1324
1327
  }
@@ -1341,11 +1344,11 @@ class v {
1341
1344
  let b = s.lastScreenCoverage * 4;
1342
1345
  this.context?.engine === "model-viewer" && (b *= 1.5);
1343
1346
  const D = k / window.devicePixelRatio * b;
1344
- let q = !1;
1347
+ let N = !1;
1345
1348
  for (let P = w.lods.length - 1; P >= 0; P--) {
1346
1349
  const L = w.lods[P];
1347
- if (!(d && L.max_height >= 2048) && !(Ae() && L.max_height > 4096) && (L.max_height > D || !q && P === 0)) {
1348
- if (q = !0, i.texture_lod = P, R && i.texture_lod < s.lastLodLevel_Texture) {
1350
+ if (!(d && L.max_height >= 2048) && !(_e() && L.max_height > 4096) && (L.max_height > D || !N && P === 0)) {
1351
+ if (N = !0, i.texture_lod = P, R && i.texture_lod < s.lastLodLevel_Texture) {
1349
1352
  const x = L.max_height;
1350
1353
  console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${i.texture_lod} = ${x}px
1351
1354
  Screensize: ${D.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${g.toFixed(1)}
@@ -1367,13 +1370,13 @@ class yt {
1367
1370
  lastScreenspaceVolume = new A();
1368
1371
  lastCentrality = 0;
1369
1372
  }
1370
- const be = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ie = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
1373
+ const Me = /* @__PURE__ */ Symbol("NEEDLE_mesh_lod"), ie = /* @__PURE__ */ Symbol("NEEDLE_texture_lod");
1371
1374
  let he = null;
1372
1375
  function Re() {
1373
1376
  const n = xt();
1374
1377
  n && (n.mapURLs(function(t) {
1375
- return Me(), t;
1376
- }), Me(), he?.disconnect(), he = new MutationObserver((t) => {
1378
+ return De(), t;
1379
+ }), De(), he?.disconnect(), he = new MutationObserver((t) => {
1377
1380
  t.forEach((e) => {
1378
1381
  e.addedNodes.forEach((s) => {
1379
1382
  s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ie(s);
@@ -1388,18 +1391,18 @@ function xt() {
1388
1391
  console.debug("[gltf-progressive] model-viewer defined"), Re();
1389
1392
  }), null);
1390
1393
  }
1391
- function Me() {
1394
+ function De() {
1392
1395
  if (typeof document > "u") return;
1393
1396
  document.querySelectorAll("model-viewer").forEach((t) => {
1394
1397
  Ie(t);
1395
1398
  });
1396
1399
  }
1397
- const De = /* @__PURE__ */ new WeakSet();
1400
+ const Oe = /* @__PURE__ */ new WeakSet();
1398
1401
  let wt = 0;
1399
1402
  function Ie(n) {
1400
- if (!n || De.has(n))
1403
+ if (!n || Oe.has(n))
1401
1404
  return null;
1402
- De.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++wt + `
1405
+ Oe.add(n), console.debug("[gltf-progressive] found new model-viewer..." + ++wt + `
1403
1406
  `, n.getAttribute("src"));
1404
1407
  let t = null, e = null, s = null;
1405
1408
  for (let r = n; r != null; r = Object.getPrototypeOf(r)) {
@@ -1481,8 +1484,8 @@ class _t {
1481
1484
  }
1482
1485
  }
1483
1486
  tryParseMeshLOD(t, e) {
1484
- if (e[be] == !0) return;
1485
- e[be] = !0;
1487
+ if (e[Me] == !0) return;
1488
+ e[Me] = !0;
1486
1489
  const s = this.tryGetCurrentModelViewer(t), r = this.getUrl(s);
1487
1490
  if (!r)
1488
1491
  return;
@@ -1508,7 +1511,7 @@ function Lt(...n) {
1508
1511
  default:
1509
1512
  throw new Error("Invalid arguments");
1510
1513
  }
1511
- we(e), Pe(s), Ce(s, {
1514
+ we(e), Te(s), Ae(s, {
1512
1515
  progressive: !0,
1513
1516
  ...r?.hints
1514
1517
  }), s.register((o) => new m(o));
@@ -1521,7 +1524,7 @@ if (!nt) {
1521
1524
  gltfProgressive: {
1522
1525
  useNeedleProgressive: Lt,
1523
1526
  LODsManager: v,
1524
- configureLoader: Ce,
1527
+ configureLoader: Ae,
1525
1528
  getRaycastMesh: ee,
1526
1529
  useRaycastMeshes: lt
1527
1530
  }
@@ -1537,8 +1540,8 @@ export {
1537
1540
  v as LODsManager,
1538
1541
  m as NEEDLE_progressive,
1539
1542
  Ke as VERSION,
1540
- Pe as addDracoAndKTX2Loaders,
1541
- Ce as configureLoader,
1543
+ Te as addDracoAndKTX2Loaders,
1544
+ Ae as configureLoader,
1542
1545
  we as createLoaders,
1543
1546
  ee as getRaycastMesh,
1544
1547
  Re as patchModelViewer,
@@ -1,10 +1,10 @@
1
- import{BufferGeometry as U,Mesh as F,Box3 as ie,Vector3 as A,Sphere as Le,CompressedTexture as je,Texture as $,Matrix3 as We,InterleavedBuffer as Ue,InterleavedBufferAttribute as Fe,BufferAttribute as ze,TextureLoader as qe,Matrix4 as _e,Clock as Ve,MeshStandardMaterial as Xe}from"three";import{GLTFLoader as ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ke}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ye}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const be="";globalThis.GLTF_PROGRESSIVE_VERSION=be,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Qe=k,Je=z,Me=new URL(k+"draco_decoder.js");Me.searchParams.append("range","true"),fetch(Me,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Qe&&De("./include/draco/"),z===Je&&Oe("./include/ktx2/")}).finally(()=>{Se()});const Ze=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function De(i){k=i,T&&T[ue]!=k?(console.debug("Updating Draco decoder path to "+i),T[ue]=k,T.setDecoderPath(k),T.preload()):console.debug("Setting Draco decoder path to "+i)}function Oe(i){z=i,I&&I.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),I.setTranscoderPath(z),I.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Se(),i?I.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:I,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(I),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let T,te,I;function Se(){T||(T=new Ye,T[ue]=k,T.setDecoderPath(k),T.setDecoderConfig({type:"js"}),T.preload()),I||(I=new He,I.setTranscoderPath(z),I.init()),te||(te=Ke)}const ce=new WeakMap;function de(i,t){let e=ce.get(i);e?e=Object.assign(e,t):e=t,ce.set(i,e)}const et=ae.prototype.load;function tt(...i){const t=ce.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return i[0]=e,et?.call(this,...i)}ae.prototype.load=tt,N("debugprogressive");function N(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function st(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Te(){return se!==void 0||(se=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",se)),se}let se;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class rt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}function ot(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,o=Math.floor(Math.log2(Math.max(t,e,s)))+1,r=nt(i);return t*e*s*r*(1-Math.pow(.25,o))/(1-.25)}function nt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const o=i.type;return o===1009||o===1010?s=1:o===1011||o===1012?s=2:o===1013||o===1014||o===1015?s=4:o===1016&&(s=2),t*s}const it=typeof window>"u"&&typeof document>"u",he=Symbol("needle:raycast-mesh");function V(i){return i?.[he]instanceof U?i[he]:null}function Ce(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=at(t);e.userData={isRaycastMesh:!0},i[he]=e}}function Ae(i=!0){if(i){if(K)return;const t=K=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!K)return;F.prototype.raycast=K,K=null}}let K=null;function at(i){const t=new U;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const j=new Array,h=N("debugprogressive");let Y,q=-1;if(h){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Y=!Y,console.log(`Toggle wireframe [${Y}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function ke(i){if(h&&Y!==void 0)if(Array.isArray(i))for(const t of i)ke(t);else i&&"wireframe"in i&&(i.wireframe=Y===!0)}const H=new Array;let lt=0;const ut=Te()?2:10;function ct(i){if(H.length<ut){const e=H.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const s=ve.createWorker(i||{});return H.push(s),s}const t=lt++%H.length;return H[t]}class ve{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){dt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new ve(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Ze();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=ee(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const n={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function dt(i){for(const t of i.geometries){const e=t.geometry,s=new U;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(ge(o))}for(const o in e.attributes){const r=e.attributes[o],n=ge(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(n=>ge(n));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ie,s.boundingBox.min=new A(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new A(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new Le(new A(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new je(o,r,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new $(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new We(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ge(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new Ue(s,e.stride);t=new Fe(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new ze(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ht=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const X=N("gltf-progressive-gc"),fe=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class f{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const y=s.lodInfos.get(u.key);if(y&&y.lods){a.min_count=Math.min(a.min_count,y.lods.length),a.max_count=Math.max(a.max_count,y.lods.length);for(let p=0;p<y.lods.length;p++){const w=y.lods[p];w.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,w.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,w.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of j)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,f.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:h&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(y=>(y&&n.uniforms[l].value!=y&&(n.uniforms[l].value=y,n.uniformsNeedUpdate=!0),y));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],y=r[a];u&&u.isTexture===!0?l.push({material:s,slot:y,texture:u,level:e}):l.push({material:s,slot:y,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):f.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),r&&r!==n&&((h||X)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,r.uuid),r.dispose()),null}if(this.trackTextureUsage(r),n&&n!==r&&this.untrackTextureUsage(n)&&(h||X)){const l=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} \u2192 ${e} for ${s.name||s.type}.${o}`,n.uuid)}s[o]=r}return r}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&f.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,f.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&f.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&f.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[fe]=r);const n=r.guid;f.assignLODInformation(t,e,n,s,o),f.lodInfos.set(n,r),f.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),f.assignLODInformation(t,l,e,o,r),f.lodInfos.set(e,n);let a=f.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],f.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Ce(s,l);for(const u of j)u.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(h||X)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
1
+ import{BufferGeometry as U,Mesh as F,Box3 as ie,Vector3 as A,Sphere as _e,CompressedTexture as je,Texture as $,Matrix3 as We,InterleavedBuffer as Ue,InterleavedBufferAttribute as Fe,BufferAttribute as ze,TextureLoader as qe,Matrix4 as be,Clock as Ve,MeshStandardMaterial as Xe}from"three";import{GLTFLoader as ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ke}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ye}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const Me="";globalThis.GLTF_PROGRESSIVE_VERSION=Me,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Qe=k,Je=z,De=new URL(k+"draco_decoder.js");De.searchParams.append("range","true"),fetch(De,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Qe&&Oe("./include/draco/"),z===Je&&Se("./include/ktx2/")}).finally(()=>{Te()});const Ze=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function Oe(i){k=i,T&&T[ue]!=k?(console.debug("Updating Draco decoder path to "+i),T[ue]=k,T.setDecoderPath(k),T.preload()):console.debug("Setting Draco decoder path to "+i)}function Se(i){z=i,I&&I.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),I.setTranscoderPath(z),I.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Te(),i?I.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:I,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(I),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let T,te,I;function Te(){T||(T=new Ye,T[ue]=k,T.setDecoderPath(k),T.setDecoderConfig({type:"js"}),T.preload()),I||(I=new He,I.setTranscoderPath(z),I.init()),te||(te=Ke)}const ce=new WeakMap;function de(i,t){let e=ce.get(i);e?e=Object.assign(e,t):e=t,ce.set(i,e)}const et=ae.prototype.load;function tt(...i){const t=ce.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return i[0]=e,et?.call(this,...i)}ae.prototype.load=tt,N("debugprogressive");function N(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function st(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function he(){return se!==void 0||(se=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",se)),se}let se;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class rt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}function ot(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,o=Math.floor(Math.log2(Math.max(t,e,s)))+1,r=nt(i);return t*e*s*r*(1-Math.pow(.25,o))/(1-.25)}function nt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const o=i.type;return o===1009||o===1010?s=1:o===1011||o===1012?s=2:o===1013||o===1014||o===1015?s=4:o===1016&&(s=2),t*s}const it=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function V(i){return i?.[ge]instanceof U?i[ge]:null}function Ce(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=at(t);e.userData={isRaycastMesh:!0},i[ge]=e}}function Ae(i=!0){if(i){if(K)return;const t=K=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!K)return;F.prototype.raycast=K,K=null}}let K=null;function at(i){const t=new U;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const j=new Array,h=N("debugprogressive");let Y,q=-1;if(h){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Y=!Y,console.log(`Toggle wireframe [${Y}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function ke(i){if(h&&Y!==void 0)if(Array.isArray(i))for(const t of i)ke(t);else i&&"wireframe"in i&&(i.wireframe=Y===!0)}const H=new Array;let lt=0;const ut=he()?2:10;function ct(i){if(H.length<ut){const e=H.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const s=Le.createWorker(i||{});return H.push(s),s}const t=lt++%H.length;return H[t]}class Le{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){dt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Le(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Ze();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=ee(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const n={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function dt(i){for(const t of i.geometries){const e=t.geometry,s=new U;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(fe(o))}for(const o in e.attributes){const r=e.attributes[o],n=fe(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(n=>fe(n));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ie,s.boundingBox.min=new A(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new A(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new _e(new A(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new je(o,r,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new $(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new We(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function fe(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new Ue(s,e.stride);t=new Fe(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new ze(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ht=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const X=N("gltf-progressive-gc"),me=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class f{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const y=s.lodInfos.get(u.key);if(y&&y.lods){a.min_count=Math.min(a.min_count,y.lods.length),a.max_count=Math.max(a.max_count,y.lods.length);for(let p=0;p<y.lods.length;p++){const w=y.lods[p];w.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,w.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,w.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of j)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,f.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:h&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(y=>(y&&n.uniforms[l].value!=y&&(n.uniforms[l].value=y,n.uniformsNeedUpdate=!0),y));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],y=r[a];u&&u.isTexture===!0?l.push({material:s,slot:y,texture:u,level:e}):l.push({material:s,slot:y,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):f.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),r&&r!==n&&((h||X)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,r.uuid),r.dispose()),null}if(this.trackTextureUsage(r),n&&n!==r&&this.untrackTextureUsage(n)&&(h||X)){const l=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} \u2192 ${e} for ${s.name||s.type}.${o}`,n.uuid)}s[o]=r}return r}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&f.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,f.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&f.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&f.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[me]=r);const n=r.guid;f.assignLODInformation(t,e,n,s,o),f.lodInfos.set(n,r),f.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),f.assignLODInformation(t,l,e,o,r),f.lodInfos.set(e,n);let a=f.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],f.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Ce(s,l);for(const u of j)u.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const o=s;this.textureRefCounts.delete(o.uuid),o.dispose()}else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(h||X)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
2
  ${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(h||X)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} \u2192 ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||X)&&r("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const o=s-1;if(o<=0)return this.textureRefCounts.delete(e),(h||X)&&r("[gltf-progressive] Memory: Dispose texture",o),t.dispose(),!0;return this.textureRefCounts.set(e,o),h==="verbose"&&r("[gltf-progressive] Memory: Untrack texture",o),!1;function r(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const y=a&&u?`${a}x${u}`:"N/A";let p="N/A";a&&u&&(p=`~${(ot(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} \u2014 ${t.name} ${y} (${p}), refCount: ${s} \u2192 ${l}
3
- ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[fe]&&(n=l.source[fe])}if(n||(n=f.lodInfos.get(r)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let u=!1;const y=Array.isArray(n.lods);if(y&&e>=n.lods.length?u=!0:y||(u=!0),u){const p=this.lowresCache.get(r);if(p){const w=p.deref();if(w)return w;this.lowresCache.delete(r),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=st(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,y=await this.queue.slot(a),p=this.cache.get(u);if(p!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),p instanceof WeakRef){const c=p.deref();if(c){let x=c,L=!1;if(x instanceof $&&t instanceof $?x.image?.data||x.source?.data?x=this.copySettings(t,x):L=!0:x instanceof U&&t instanceof U&&(x.attributes.position?.array||(L=!0)),!L)return x}this.cache.delete(u),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await p.catch(L=>(console.error(`Error loading LOD ${e} from ${a}
3
+ ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[me]&&(n=l.source[me])}if(n||(n=f.lodInfos.get(r)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let u=!1;const y=Array.isArray(n.lods);if(y&&e>=n.lods.length?u=!0:y||(u=!0),u){const p=this.lowresCache.get(r);if(p){const w=p.deref();if(w)return w;this.lowresCache.delete(r),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=st(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,y=await this.queue.slot(a),p=this.cache.get(u);if(p!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),p instanceof WeakRef){const c=p.deref();if(c){let x=c,L=!1;if(x instanceof $&&t instanceof $?x.image?.data||x.source?.data?x=this.copySettings(t,x):L=!0:x instanceof U&&t instanceof U&&(x.attributes.position?.array||(L=!0)),!L)return x}this.cache.delete(u),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await p.catch(L=>(console.error(`Error loading LOD ${e} from ${a}
4
4
  `,L),null)),x=!1;if(c==null||(c instanceof $&&t instanceof $?c.image?.data||c.source?.data?c=this.copySettings(t,c):(x=!0,this.cache.delete(u)):c instanceof U&&t instanceof U&&(c.attributes.position?.array||(x=!0,this.cache.delete(u)))),!x)return c}if(!y.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const w=n,P=new Promise(async(c,x)=>{if(ht){const g=await(await ct({})).load(a);if(g.textures.length>0)for(const d of g.textures){let m=d.texture;return f.assignLODInformation(o.url,m,r,e,void 0),t instanceof $&&(m=this.copySettings(t,m)),m&&(m.guid=w.guid),c(m)}if(g.geometries.length>0){const d=new Array;for(const m of g.geometries){const _=m.geometry;f.assignLODInformation(o.url,_,r,e,m.primitiveIndex),d.push(_)}return c(d)}return c(null)}const L=new ae;le(L),h&&(await new Promise(g=>setTimeout(g,1e3)),s&&console.warn("Start loading (delayed) "+a,w.guid));let C=a;if(w&&Array.isArray(w.lods)){const g=w.lods[e];g.hash&&(C+="?v="+g.hash)}const M=await L.loadAsync(C).catch(g=>(console.error(`Error loading LOD ${e} from ${a}
5
- `,g),c(null)));if(!M)return c(null);const R=M.parser;s&&console.log("Loading finished "+a,w.guid);let D=0;if(M.parser.json.textures){let g=!1;for(const d of M.parser.json.textures){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){let d=await R.getDependency("texture",D);return d&&f.assignLODInformation(o.url,d,r,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,D,d,u),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=w.guid),c(d)}else h&&console.warn("Could not find texture with guid",w.guid,M.parser.json)}if(D=0,M.parser.json.meshes){let g=!1;for(const d of M.parser.json.meshes){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){const d=await R.getDependency("mesh",D);if(s&&console.log(`Loaded Mesh "${d.name}"`,a,D,d,u),d.isMesh===!0){const m=d.geometry;return f.assignLODInformation(o.url,m,r,e,0),c(m)}else{const m=new Array;for(let _=0;_<d.children.length;_++){const S=d.children[_];if(S.isMesh===!0){const G=S.geometry;f.assignLODInformation(o.url,G,r,e,_),m.push(G)}}return c(m)}}else h&&console.warn("Could not find mesh with guid",w.guid,M.parser.json)}return c(null)});this.cache.set(u,P),y.use(P);const b=await P;return b!=null?b instanceof $?(this.cache.set(u,new WeakRef(b)),f._resourceRegistry.register(b,u)):Array.isArray(b)?this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(null)),b}else if(t instanceof $){s&&console.log("Load texture from uri: "+a);const u=await new qe().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,s&&console.log(n,u)):h&&console.warn("failed loading",a),u}}return null}static queue=new rt(50,{debug:h!=!1});static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const n=new gt(t,s,o,r);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
5
+ `,g),c(null)));if(!M)return c(null);const R=M.parser;s&&console.log("Loading finished "+a,w.guid);let D=0;if(M.parser.json.textures){let g=!1;for(const d of M.parser.json.textures){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){let d=await R.getDependency("texture",D);return d&&f.assignLODInformation(o.url,d,r,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,D,d,u),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=w.guid),c(d)}else h&&console.warn("Could not find texture with guid",w.guid,M.parser.json)}if(D=0,M.parser.json.meshes){let g=!1;for(const d of M.parser.json.meshes){if(d?.extensions){const m=d?.extensions[B];if(m?.guid&&m.guid===w.guid){g=!0;break}}D++}if(g){const d=await R.getDependency("mesh",D);if(s&&console.log(`Loaded Mesh "${d.name}"`,a,D,d,u),d.isMesh===!0){const m=d.geometry;return f.assignLODInformation(o.url,m,r,e,0),c(m)}else{const m=new Array;for(let _=0;_<d.children.length;_++){const S=d.children[_];if(S.isMesh===!0){const G=S.geometry;f.assignLODInformation(o.url,G,r,e,_),m.push(G)}}return c(m)}}else h&&console.warn("Could not find mesh with guid",w.guid,M.parser.json)}return c(null)});this.cache.set(u,P),y.use(P);const b=await P;return b!=null?b instanceof $?(this.cache.set(u,new WeakRef(b)),f._resourceRegistry.register(b,u)):Array.isArray(b)?this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(null)),b}else if(t instanceof $){s&&console.log("Load texture from uri: "+a);const u=await new qe().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,s&&console.log(n,u)):h&&console.warn("failed loading",a),u}}return null}static _queue;static get queue(){return this._queue??=new rt(he()?20:50,{debug:h!=!1})}static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const n=new gt(t,s,o,r);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
6
6
  `,t.uuid,`
7
- `,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class gt{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class me{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=N("debugprogressive"),ft=N("noprogressive"),pe=Symbol("Needle:LODSManager"),xe=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static getObjectLODState(t){return t[xe]}static addPlugin(t){j.push(t)}static removePlugin(t){const e=j.indexOf(t);e>=0&&j.splice(e,1)}static get(t,e){if(t[pe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[pe];const s=new v(t,{engine:"unknown",...e});return t[pe]=s,s}renderer;context;projectionScreenMatrix=new _e;get plugins(){return j}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new me(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Ve;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ee(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,E&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){E&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(E==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const y=Math.random()*16777215,p=new Xe({color:y});a.object.material=p}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[xe];if(r||(r=new mt,s[xe]=r),r.frames++<2)return;for(const l of j)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;n>=0?(O.mesh_lod=n,O.texture_lod=n):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,n),h&&s.material&&!s.isGizmo&&ke(s.material);for(const l of j)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=f.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});me.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,n=f.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return me.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new Le;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new _e;_tempWorldPosition=new A;_tempBoxSize=new A;_tempBox2Size=new A;static corner0=new A;static corner1=new A;static corner2=new A;static corner3=new A;static _tempPtInside=new A;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=11,l=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),y=a&&a.length>0,p=f.getMaterialMinMaxLODsCount(e.material),w=p.min_count!==1/0&&p.min_count>=0&&p.max_count>=0;if(!y&&!w){r.mesh_lod=0,r.texture_lod=0;return}y||(l=!0,n=0);const P=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let b=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[v.$skinnedMeshBoundsOffset]){const L=v.skinnedMeshBoundsFrameOffsetCounter++;c[v.$skinnedMeshBoundsOffset]=L}const x=c[v.$skinnedMeshBoundsOffset];if((s.frames+x)%this.skinnedMeshAutoUpdateBoundsInterval===0){const L=V(c),C=c.geometry;L&&(c.geometry=L),c.computeBoundingBox(),c.geometry=C}}b=c.boundingBox}if(b){const c=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(b),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const g=this._tempBox.min,d=this._tempBox.max;let m=g.x,_=g.y,S=d.x,G=d.y;const Q=2,oe=1.5,J=(g.x+d.x)*.5,Z=(g.y+d.y)*.5;m=(m-J)*Q+J,_=(_-Z)*Q+Z,S=(S-J)*Q+J,G=(G-Z)*Q+Z;const Ge=m<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(d.x)),Ne=_<0&&G>0?0:Math.min(Math.abs(g.y),Math.abs(d.y)),ne=Math.max(Ge,Ne);s.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else s.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&P>0&&x.multiplyScalar(P/screen.availHeight),t.isPerspectiveCamera?x.x*=t.aspect:t.isOrthographicCamera;const L=t.matrixWorldInverse,C=this._tempBox2;C.copy(b),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(L);const M=C.getSize(this._tempBox2Size),R=Math.max(M.x,M.y);if(Math.max(x.x,x.y)!=0&&R!=0&&(x.z=M.z/Math.max(M.x,M.y)*Math.max(x.x,x.y)),s.lastScreenCoverage=Math.max(x.x,x.y,x.z),s.lastScreenspaceVolume.copy(x),s.lastScreenCoverage*=s.lastCentrality,E&&v.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const d=v.corner0,m=v.corner1,_=v.corner2,S=v.corner3;d.copy(this._tempBox.min),m.copy(this._tempBox.max),m.x=d.x,_.copy(this._tempBox.max),_.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=m.z=_.z=S.z=G,d.applyMatrix4(g),m.applyMatrix4(g),_.applyMatrix4(g),S.applyMatrix4(g),v.debugDrawLine(d,m,255),v.debugDrawLine(d,_,255),v.debugDrawLine(m,S,255),v.debugDrawLine(_,S,255)}let D=999;if(a&&s.lastScreenCoverage>0)for(let g=0;g<a.length;g++){const d=a[g],m=(d.densities?.[u]||d.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!d.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),m<o){D=g;break}}D<n&&(n=D,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,E&&r.mesh_lod!=s.lastLodLevel_Mesh){const c=a?.[r.mesh_lod];c&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(w){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=p.max_count-1,E){const x=p.lods[p.max_count-1];E&&console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`)}}else{const x=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let L=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(L*=1.5);const C=P/window.devicePixelRatio*L;let M=!1;for(let R=p.lods.length-1;R>=0;R--){const D=p.lods[R];if(!(c&&D.max_height>=2048)&&!(Te()&&D.max_height>4096)&&(D.max_height>C||!M&&R===0)){if(M=!0,r.texture_lod=R,E&&r.texture_lod<s.lastLodLevel_Texture){const g=D.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${g}px
7
+ `,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class gt{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class pe{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=N("debugprogressive"),ft=N("noprogressive"),xe=Symbol("Needle:LODSManager"),ye=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static getObjectLODState(t){return t[ye]}static addPlugin(t){j.push(t)}static removePlugin(t){const e=j.indexOf(t);e>=0&&j.splice(e,1)}static get(t,e){if(t[xe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[xe];const s=new v(t,{engine:"unknown",...e});return t[xe]=s,s}renderer;context;projectionScreenMatrix=new be;get plugins(){return j}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new pe(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Ve;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ee(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,E&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){E&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(E==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const y=Math.random()*16777215,p=new Xe({color:y});a.object.material=p}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[ye];if(r||(r=new mt,s[ye]=r),r.frames++<2)return;for(const l of j)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;n>=0?(O.mesh_lod=n,O.texture_lod=n):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,n),h&&s.material&&!s.isGizmo&&ke(s.material);for(const l of j)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=f.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,n=f.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return pe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new _e;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new be;_tempWorldPosition=new A;_tempBoxSize=new A;_tempBox2Size=new A;static corner0=new A;static corner1=new A;static corner2=new A;static corner3=new A;static _tempPtInside=new A;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=11,l=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),y=a&&a.length>0,p=f.getMaterialMinMaxLODsCount(e.material),w=p.min_count!==1/0&&p.min_count>=0&&p.max_count>=0;if(!y&&!w){r.mesh_lod=0,r.texture_lod=0;return}y||(l=!0,n=0);const P=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let b=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[v.$skinnedMeshBoundsOffset]){const L=v.skinnedMeshBoundsFrameOffsetCounter++;c[v.$skinnedMeshBoundsOffset]=L}const x=c[v.$skinnedMeshBoundsOffset];if((s.frames+x)%this.skinnedMeshAutoUpdateBoundsInterval===0){const L=V(c),C=c.geometry;L&&(c.geometry=L),c.computeBoundingBox(),c.geometry=C}}b=c.boundingBox}if(b){const c=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(b),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const g=this._tempBox.min,d=this._tempBox.max;let m=g.x,_=g.y,S=d.x,G=d.y;const Q=2,oe=1.5,J=(g.x+d.x)*.5,Z=(g.y+d.y)*.5;m=(m-J)*Q+J,_=(_-Z)*Q+Z,S=(S-J)*Q+J,G=(G-Z)*Q+Z;const Ge=m<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(d.x)),Ne=_<0&&G>0?0:Math.min(Math.abs(g.y),Math.abs(d.y)),ne=Math.max(Ge,Ne);s.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else s.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&P>0&&x.multiplyScalar(P/screen.availHeight),t.isPerspectiveCamera?x.x*=t.aspect:t.isOrthographicCamera;const L=t.matrixWorldInverse,C=this._tempBox2;C.copy(b),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(L);const M=C.getSize(this._tempBox2Size),R=Math.max(M.x,M.y);if(Math.max(x.x,x.y)!=0&&R!=0&&(x.z=M.z/Math.max(M.x,M.y)*Math.max(x.x,x.y)),s.lastScreenCoverage=Math.max(x.x,x.y,x.z),s.lastScreenspaceVolume.copy(x),s.lastScreenCoverage*=s.lastCentrality,E&&v.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const d=v.corner0,m=v.corner1,_=v.corner2,S=v.corner3;d.copy(this._tempBox.min),m.copy(this._tempBox.max),m.x=d.x,_.copy(this._tempBox.max),_.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=m.z=_.z=S.z=G,d.applyMatrix4(g),m.applyMatrix4(g),_.applyMatrix4(g),S.applyMatrix4(g),v.debugDrawLine(d,m,255),v.debugDrawLine(d,_,255),v.debugDrawLine(m,S,255),v.debugDrawLine(_,S,255)}let D=999;if(a&&s.lastScreenCoverage>0)for(let g=0;g<a.length;g++){const d=a[g],m=(d.densities?.[u]||d.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!d.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),m<o){D=g;break}}D<n&&(n=D,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,E&&r.mesh_lod!=s.lastLodLevel_Mesh){const c=a?.[r.mesh_lod];c&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(w){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=p.max_count-1,E){const x=p.lods[p.max_count-1];E&&console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`)}}else{const x=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let L=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(L*=1.5);const C=P/window.devicePixelRatio*L;let M=!1;for(let R=p.lods.length-1;R>=0;R--){const D=p.lods[R];if(!(c&&D.max_height>=2048)&&!(he()&&D.max_height>4096)&&(D.max_height>C||!M&&R===0)){if(M=!0,r.texture_lod=R,E&&r.texture_lod<s.lastLodLevel_Texture){const g=D.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${g}px
8
8
  Screensize: ${C.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
9
- ${e.name}`)}break}}}}else r.texture_lod=0}}class mt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new A;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let ye=null;function we(){const i=pt();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),ye?.disconnect(),ye=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),ye.observe(document,{childList:!0,subtree:!0}))}function pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function Ee(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{$e(i)})}const Re=new WeakSet;let xt=0;function $e(i){if(!i||Re.has(i))return null;Re.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++xt+`
10
- `,i.getAttribute("src"));let t=null,e=null,s=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),n=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let o=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=v.get(t,{engine:"model-viewer"});return v.addPlugin(new yt),r.enable(),r.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class yt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[re]==!0)return;e[re]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let y=0;y<u.length;y++){const p=u[y],w=a[p];if(w?.isTexture===!0){const P=w.userData?.associations?.textures;if(P==null)continue;const b=s.parser.json.textures[P];if(!b){console.warn("Texture data not found for texture index "+P);continue}if(b?.extensions?.[B]){const c=b.extensions[B];c&&r&&f.registerTexture(r,w,c.lods.length,P,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ie]==!0)return;e[Ie]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[B];if(r&&o){const n=e.uuid;f.registerMesh(o,n,e,0,r.lods.length,r)}}}function Be(...i){let t,e,s,o;switch(i.length){case 2:[s,e]=i,o={};break;case 3:[s,e,o]=i;break;case 4:[t,e,s,o]=i;break;default:throw new Error("Invalid arguments")}ee(e),le(s),de(s,{progressive:!0,...o?.hints}),s.register(n=>new f(n));const r=v.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}if(we(),!it){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:v,configureLoader:de,getRaycastMesh:V,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{B as EXTENSION_NAME,v as LODsManager,f as NEEDLE_progressive,be as VERSION,le as addDracoAndKTX2Loaders,de as configureLoader,ee as createLoaders,V as getRaycastMesh,we as patchModelViewer,Ce as registerRaycastMesh,De as setDracoDecoderLocation,Oe as setKTX2TranscoderLocation,Be as useNeedleProgressive,Ae as useRaycastMeshes};
9
+ ${e.name}`)}break}}}}else r.texture_lod=0}}class mt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new A;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let we=null;function ve(){const i=pt();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),we?.disconnect(),we=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),we.observe(document,{childList:!0,subtree:!0}))}function pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),ve()}),null)}function Ee(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{$e(i)})}const Re=new WeakSet;let xt=0;function $e(i){if(!i||Re.has(i))return null;Re.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++xt+`
10
+ `,i.getAttribute("src"));let t=null,e=null,s=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),n=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let o=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=v.get(t,{engine:"model-viewer"});return v.addPlugin(new yt),r.enable(),r.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class yt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[re]==!0)return;e[re]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let y=0;y<u.length;y++){const p=u[y],w=a[p];if(w?.isTexture===!0){const P=w.userData?.associations?.textures;if(P==null)continue;const b=s.parser.json.textures[P];if(!b){console.warn("Texture data not found for texture index "+P);continue}if(b?.extensions?.[B]){const c=b.extensions[B];c&&r&&f.registerTexture(r,w,c.lods.length,P,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ie]==!0)return;e[Ie]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[B];if(r&&o){const n=e.uuid;f.registerMesh(o,n,e,0,r.lods.length,r)}}}function Be(...i){let t,e,s,o;switch(i.length){case 2:[s,e]=i,o={};break;case 3:[s,e,o]=i;break;case 4:[t,e,s,o]=i;break;default:throw new Error("Invalid arguments")}ee(e),le(s),de(s,{progressive:!0,...o?.hints}),s.register(n=>new f(n));const r=v.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}if(ve(),!it){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:v,configureLoader:de,getRaycastMesh:V,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{B as EXTENSION_NAME,v as LODsManager,f as NEEDLE_progressive,Me as VERSION,le as addDracoAndKTX2Loaders,de as configureLoader,ee as createLoaders,V as getRaycastMesh,ve as patchModelViewer,Ce as registerRaycastMesh,Oe as setDracoDecoderLocation,Se as setKTX2TranscoderLocation,Be as useNeedleProgressive,Ae as useRaycastMeshes};
@@ -1,10 +1,10 @@
1
- "use strict";var Ue=Object.create;var _e=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var ze=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var qe=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ze(t))!Ne.call(i,r)&&r!==e&&_e(i,r,{get:()=>t[r],enumerable:!(s=We(t,r))||s.enumerable});return i};var Ee=(i,t,e)=>(e=i!=null?Ue(Ve(i)):{},qe(t||!i||!i.__esModule?_e(e,"default",{value:i,enumerable:!0}):e,i));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js"),je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Xe=require("three/examples/jsm/loaders/DRACOLoader.js"),Ke=require("three/examples/jsm/loaders/KTX2Loader.js");var ne=typeof document<"u"?document.currentScript:null;const Oe="";globalThis.GLTF_PROGRESSIVE_VERSION=Oe;console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",q="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ye=I,He=q,Se=new URL(I+"draco_decoder.js");Se.searchParams.append("range","true");fetch(Se,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===Ye&&Te("./include/draco/"),q===He&&Pe("./include/ktx2/")}).finally(()=>{Ce()});const Qe=()=>({dracoDecoderPath:I,ktx2TranscoderPath:q});function Te(i){I=i,A&&A[he]!=I?(console.debug("Updating Draco decoder path to "+i),A[he]=I,A.setDecoderPath(I),A.preload()):console.debug("Setting Draco decoder path to "+i)}function Pe(i){q=i,$&&$.transcoderPath!=q?(console.debug("Updating KTX2 transcoder path to "+i),$.setTranscoderPath(q),$.init()):console.debug("Setting KTX2 transcoder path to "+i)}function re(i){return Ce(),i?$.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:$,meshoptDecoder:se}}function ye(i){i.dracoLoader||i.setDRACOLoader(A),i.ktx2Loader||i.setKTX2Loader($),i.meshoptDecoder||i.setMeshoptDecoder(se)}const he=Symbol("dracoDecoderPath");let A,se,$;function Ce(){A||(A=new Xe.DRACOLoader,A[he]=I,A.setDecoderPath(I),A.setDecoderConfig({type:"js"}),A.preload()),$||($=new Ke.KTX2Loader,$.setTranscoderPath(q),$.init()),se||(se=je.MeshoptDecoder)}const ge=new WeakMap;function xe(i,t){let e=ge.get(i);e?e=Object.assign(e,t):e=t,ge.set(i,e)}const Je=me.GLTFLoader.prototype.load;function Ze(...i){const t=ge.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,n=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${n}`:this.requestHeader.Accept=`*/*;usecase=${n}`,e=s.toString()}return i[0]=e,Je?.call(this,...i)}me.GLTFLoader.prototype.load=Ze;z("debugprogressive");function z(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function et(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Ae(){return K!==void 0||(K=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",K)),K}let K;function ve(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class tt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:r}=this._queue.shift();r({use:o=>this.add(s,o)})}}}function st(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,o=rt(i);return t*e*s*o*(1-Math.pow(.25,r))/(1-.25)}function rt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=i.type;return r===1009||r===1010?s=1:r===1011||r===1012?s=2:r===1013||r===1014||r===1015?s=4:r===1016&&(s=2),t*s}const ot=typeof window>"u"&&typeof document>"u",pe=Symbol("needle:raycast-mesh");function j(i){return i?.[pe]instanceof c.BufferGeometry?i[pe]:null}function Re(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!j(i)){const s=it(t);s.userData={isRaycastMesh:!0},i[pe]=s}}function ke(i=!0){if(i){if(Y)return;const t=Y=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const r=this,o=j(r);let n;o&&r.isMesh&&(n=r.geometry,r.geometry=o),t.call(this,e,s),n&&(r.geometry=n)}}else{if(!Y)return;c.Mesh.prototype.raycast=Y,Y=null}}let Y=null;function it(i){const t=new c.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,h=z("debugprogressive");let Q,N=-1;if(h){let i=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(N=s,console.log(`Set LOD level to [${N}]`))})}function Ie(i){if(h&&Q!==void 0)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=Q===!0)}const H=new Array;let nt=0;const at=Ae()?2:10;function lt(i){if(H.length<at){const s=H.length;h&&console.warn(`[Worker] Creating new worker #${s}`);const r=we.createWorker(i||{});return H.push(r),r}const t=nt++%H.length;return H[t]}class we{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const o of this._running)if(o.url===r.result.url){ut(r.result),o.resolve(r.result);const n=o.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ne&&ne.tagName.toUpperCase()==="SCRIPT"&&ne.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new we(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Qe();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),r=await this._webglRenderer);const l=re(r).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ut(i){for(const t of i.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ae(r))}for(const r in e.attributes){const o=e.attributes[r],n=ae(o);s.setAttribute(r,n)}if(e.morphAttributes)for(const r in e.morphAttributes){const n=e.morphAttributes[r].map(l=>ae(l));s.morphAttributes[r]=n}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(r,o,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new c.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ae(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,r=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(r,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new c.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ct=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const E=z("gltf-progressive-gc"),le=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class m{get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",o=t[r];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let x=0;x<d.lods.length;x++){const L=d.lods[x];L.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,L.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,L.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const n=t[o];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const o=s.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const o of W)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,e).then(o=>{if(Array.isArray(o)){const n=r.index||0;o=o[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&s!=o&&(o?.isBufferGeometry?t.geometry=o:h&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const r=new Array;for(const o of s.material){const n=this.assignTextureLOD(o,e);r.push(n)}return Promise.all(r).then(o=>{const n=new Array;for(const l of o)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,r=[],o=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(d=>(d&&n.uniforms[l].value!=d&&(n.uniforms[l].value=d,n.uniformsNeedUpdate=!0),d));r.push(u),o.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);r.push(a),o.push(n)}}return Promise.all(r).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],d=o[a];u&&u.isTexture===!0?l.push({material:s,slot:d,texture:u,level:e}):l.push({material:s,slot:d,texture:null,level:e})}return l})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r){return t?.isTexture!==!0?Promise.resolve(null):r==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(o?.isTexture===!0){if(o!=t&&s&&r){const n=s[r];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,o),o&&o!==n&&((h||E)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,o.uuid),o.dispose()),null}if(this.trackTextureUsage(o),n&&n!==o&&this.untrackTextureUsage(n)&&(h||E)){const a=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level??"?"} → ${e} for ${s.name||s.type}.${r}`,n.uuid)}s[r]=o}return o}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r){if(!r.lods){h&&console.warn("Texture has no LODs",r);return}let o=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(o=!0,m.registerTexture(this.url,n,r.lods?.length,s,r));o||this.parser.getDependency("texture",s).then(n=>{n&&m.registerTexture(this.url,n,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r&&r.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const n=this.parser.associations.get(o);n?.meshes===s&&m.registerMesh(this.url,r.guid,o,r.lods.length,n.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,o)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[le]=o);const n=o.guid;m.assignLODInformation(t,e,n,s,r),m.lodInfos.set(n,o),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,r,o,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:o,uuid:s.uuid},n,s),m.assignLODInformation(t,l,e,r,o),m.lodInfos.set(e,n);let u=m.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],m.lowresCache.set(e,new WeakRef(u)),r>0&&!j(s)&&Re(s,l);for(const d of W)d.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(h||E)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
1
+ "use strict";var Ue=Object.create;var ve=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var ze=Object.getOwnPropertyNames;var qe=Object.getPrototypeOf,Ve=Object.prototype.hasOwnProperty;var Ne=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ze(t))!Ve.call(i,r)&&r!==e&&ve(i,r,{get:()=>t[r],enumerable:!(s=We(t,r))||s.enumerable});return i};var Ee=(i,t,e)=>(e=i!=null?Ue(qe(i)):{},Ne(t||!i||!i.__esModule?ve(e,"default",{value:i,enumerable:!0}):e,i));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js"),je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Xe=require("three/examples/jsm/loaders/DRACOLoader.js"),Ke=require("three/examples/jsm/loaders/KTX2Loader.js");var ne=typeof document<"u"?document.currentScript:null;const Se="";globalThis.GLTF_PROGRESSIVE_VERSION=Se;console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",N="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ye=I,He=N,Te=new URL(I+"draco_decoder.js");Te.searchParams.append("range","true");fetch(Te,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===Ye&&Pe("./include/draco/"),N===He&&Ce("./include/ktx2/")}).finally(()=>{Ae()});const Qe=()=>({dracoDecoderPath:I,ktx2TranscoderPath:N});function Pe(i){I=i,A&&A[he]!=I?(console.debug("Updating Draco decoder path to "+i),A[he]=I,A.setDecoderPath(I),A.preload()):console.debug("Setting Draco decoder path to "+i)}function Ce(i){N=i,$&&$.transcoderPath!=N?(console.debug("Updating KTX2 transcoder path to "+i),$.setTranscoderPath(N),$.init()):console.debug("Setting KTX2 transcoder path to "+i)}function re(i){return Ae(),i?$.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:$,meshoptDecoder:se}}function ye(i){i.dracoLoader||i.setDRACOLoader(A),i.ktx2Loader||i.setKTX2Loader($),i.meshoptDecoder||i.setMeshoptDecoder(se)}const he=Symbol("dracoDecoderPath");let A,se,$;function Ae(){A||(A=new Xe.DRACOLoader,A[he]=I,A.setDecoderPath(I),A.setDecoderConfig({type:"js"}),A.preload()),$||($=new Ke.KTX2Loader,$.setTranscoderPath(N),$.init()),se||(se=je.MeshoptDecoder)}const ge=new WeakMap;function xe(i,t){let e=ge.get(i);e?e=Object.assign(e,t):e=t,ge.set(i,e)}const Je=me.GLTFLoader.prototype.load;function Ze(...i){const t=ge.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,n=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${n}`:this.requestHeader.Accept=`*/*;usecase=${n}`,e=s.toString()}return i[0]=e,Je?.call(this,...i)}me.GLTFLoader.prototype.load=Ze;z("debugprogressive");function z(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function et(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function we(){return K!==void 0||(K=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",K)),K}let K;function Me(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class tt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:r}=this._queue.shift();r({use:o=>this.add(s,o)})}}}function st(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,o=rt(i);return t*e*s*o*(1-Math.pow(.25,r))/(1-.25)}function rt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=i.type;return r===1009||r===1010?s=1:r===1011||r===1012?s=2:r===1013||r===1014||r===1015?s=4:r===1016&&(s=2),t*s}const ot=typeof window>"u"&&typeof document>"u",pe=Symbol("needle:raycast-mesh");function j(i){return i?.[pe]instanceof c.BufferGeometry?i[pe]:null}function Re(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!j(i)){const s=it(t);s.userData={isRaycastMesh:!0},i[pe]=s}}function ke(i=!0){if(i){if(Y)return;const t=Y=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const r=this,o=j(r);let n;o&&r.isMesh&&(n=r.geometry,r.geometry=o),t.call(this,e,s),n&&(r.geometry=n)}}else{if(!Y)return;c.Mesh.prototype.raycast=Y,Y=null}}let Y=null;function it(i){const t=new c.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,h=z("debugprogressive");let Q,V=-1;if(h){let i=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(V=s,console.log(`Set LOD level to [${V}]`))})}function Ie(i){if(h&&Q!==void 0)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=Q===!0)}const H=new Array;let nt=0;const at=we()?2:10;function lt(i){if(H.length<at){const s=H.length;h&&console.warn(`[Worker] Creating new worker #${s}`);const r=Le.createWorker(i||{});return H.push(r),r}const t=nt++%H.length;return H[t]}class Le{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const o of this._running)if(o.url===r.result.url){ut(r.result),o.resolve(r.result);const n=o.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ne&&ne.tagName.toUpperCase()==="SCRIPT"&&ne.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new Le(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Qe();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),r=await this._webglRenderer);const l=re(r).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ut(i){for(const t of i.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ae(r))}for(const r in e.attributes){const o=e.attributes[r],n=ae(o);s.setAttribute(r,n)}if(e.morphAttributes)for(const r in e.morphAttributes){const n=e.morphAttributes[r].map(l=>ae(l));s.morphAttributes[r]=n}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(r,o,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new c.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return i}function ae(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,r=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(r,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new c.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ct=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const E=z("gltf-progressive-gc"),le=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class m{get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",o=t[r];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let x=0;x<d.lods.length;x++){const L=d.lods[x];L.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,L.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,L.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const n=t[o];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const o=s.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const o of W)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,e).then(o=>{if(Array.isArray(o)){const n=r.index||0;o=o[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&s!=o&&(o?.isBufferGeometry?t.geometry=o:h&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else h&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const r=new Array;for(const o of s.material){const n=this.assignTextureLOD(o,e);r.push(n)}return Promise.all(r).then(o=>{const n=new Array;for(const l of o)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,r=[],o=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(d=>(d&&n.uniforms[l].value!=d&&(n.uniforms[l].value=d,n.uniformsNeedUpdate=!0),d));r.push(u),o.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);r.push(a),o.push(n)}}return Promise.all(r).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],d=o[a];u&&u.isTexture===!0?l.push({material:s,slot:d,texture:u,level:e}):l.push({material:s,slot:d,texture:null,level:e})}return l})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r){return t?.isTexture!==!0?Promise.resolve(null):r==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(o?.isTexture===!0){if(o!=t&&s&&r){const n=s[r];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,o),o&&o!==n&&((h||E)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,o.uuid),o.dispose()),null}if(this.trackTextureUsage(o),n&&n!==o&&this.untrackTextureUsage(n)&&(h||E)){const a=this.getAssignedLODInformation(n);console.log(`[gltf-progressive] Disposed old texture LOD ${a?.level??"?"} → ${e} for ${s.name||s.type}.${r}`,n.uuid)}s[r]=o}return o}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}parser;url;constructor(t){const e=t.options.path;h&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r){if(!r.lods){h&&console.warn("Texture has no LODs",r);return}let o=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(o=!0,m.registerTexture(this.url,n,r.lods?.length,s,r));o||this.parser.getDependency("texture",s).then(n=>{n&&m.registerTexture(this.url,n,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r&&r.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const n=this.parser.associations.get(o);n?.meshes===s&&m.registerMesh(this.url,r.guid,o,r.lods.length,n.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,o)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[le]=o);const n=o.guid;m.assignLODInformation(t,e,n,s,r),m.lodInfos.set(n,o),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,r,o,n)=>{const l=s.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+s.name,{index:o,uuid:s.uuid},n,s),m.assignLODInformation(t,l,e,r,o),m.lodInfos.set(e,n);let u=m.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],m.lowresCache.set(e,new WeakRef(u)),r>0&&!j(s)&&Re(s,l);for(const d of W)d.onRegisteredNewMesh?.(s,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(h||E)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
2
  ${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(h||E)&&console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),h==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(h==="verbose"||E)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const r=s-1;if(r<=0)return this.textureRefCounts.delete(e),(h||E)&&o("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),h==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",r),!1;function o(n,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const d=a&&u?`${a}x${u}`:"N/A";let x="N/A";a&&u&&(x=`~${(st(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${n} — ${t.name} ${d} (${x}), refCount: ${s} → ${l}
3
3
  ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=h=="verbose",r=this.getAssignedLODInformation(t);if(!r)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const o=r?.key;let n;if(t.isTexture===!0){const a=t;a.source&&a.source[le]&&(n=a.source[le])}if(n||(n=m.lodInfos.get(o)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let d=!1;const x=Array.isArray(n.lods);if(x&&e>=n.lods.length?d=!0:x||(d=!0),d){const L=this.lowresCache.get(o);if(L){const _=L.deref();if(_)return _;this.lowresCache.delete(o),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`)}return null}}const a=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!a)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const u=et(r.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const d=u+"_"+n.guid,x=await this.queue.slot(u),L=this.cache.get(d);if(L!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${d}`),L instanceof WeakRef){const f=L.deref();if(f){let p=f,b=!1;if(p instanceof c.Texture&&t instanceof c.Texture?p.image?.data||p.source?.data?p=this.copySettings(t,p):b=!0:p instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(p.attributes.position?.array||(b=!0)),!b)return p}this.cache.delete(d),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${d}`)}else{let f=await L.catch(b=>(console.error(`Error loading LOD ${e} from ${u}
4
4
  `,b),null)),p=!1;if(f==null||(f instanceof c.Texture&&t instanceof c.Texture?f.image?.data||f.source?.data?f=this.copySettings(t,f):(p=!0,this.cache.delete(d)):f instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(f.attributes.position?.array||(p=!0,this.cache.delete(d)))),!p)return f}if(!x.use)return h&&console.log(`LOD ${e} was aborted: ${u}`),null;const _=n,R=new Promise(async(f,p)=>{if(ct){const w=await(await lt({})).load(u);if(w.textures.length>0)for(const g of w.textures){let y=g.texture;return m.assignLODInformation(r.url,y,o,e,void 0),t instanceof c.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=_.guid),f(y)}if(w.geometries.length>0){const g=new Array;for(const y of w.geometries){const S=y.geometry;m.assignLODInformation(r.url,S,o,e,y.primitiveIndex),g.push(S)}return f(g)}return f(null)}const b=new me.GLTFLoader;ye(b),h&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+u,_.guid));let B=u;if(_&&Array.isArray(_.lods)){const v=_.lods[e];v.hash&&(B+="?v="+v.hash)}const O=await b.loadAsync(B).catch(v=>(console.error(`Error loading LOD ${e} from ${u}
5
- `,v),f(null)));if(!O)return f(null);const V=O.parser;s&&console.log("Loading finished "+u,_.guid);let P=0;if(O.parser.json.textures){let v=!1;for(const w of O.parser.json.textures){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){let w=await V.getDependency("texture",P);return w&&m.assignLODInformation(r.url,w,o,e,void 0),s&&console.log('change "'+t.name+'" → "'+w.name+'"',u,P,w,d),t instanceof c.Texture&&(w=this.copySettings(t,w)),w&&(w.guid=_.guid),f(w)}else h&&console.warn("Could not find texture with guid",_.guid,O.parser.json)}if(P=0,O.parser.json.meshes){let v=!1;for(const w of O.parser.json.meshes){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){const w=await V.getDependency("mesh",P);if(s&&console.log(`Loaded Mesh "${w.name}"`,u,P,w,d),w.isMesh===!0){const g=w.geometry;return m.assignLODInformation(r.url,g,o,e,0),f(g)}else{const g=new Array;for(let y=0;y<w.children.length;y++){const S=w.children[y];if(S.isMesh===!0){const T=S.geometry;m.assignLODInformation(r.url,T,o,e,y),g.push(T)}}return f(g)}}else h&&console.warn("Could not find mesh with guid",_.guid,O.parser.json)}return f(null)});this.cache.set(d,R),x.use(R);const D=await R;return D!=null?D instanceof c.Texture?(this.cache.set(d,new WeakRef(D)),m._resourceRegistry.register(D,d)):Array.isArray(D)?this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(null)),D}else if(t instanceof c.Texture){s&&console.log("Load texture from uri: "+u);const x=await new c.TextureLoader().loadAsync(u);return x?(x.guid=n.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,s&&console.log(n,x)):h&&console.warn("failed loading",u),x}}return null}static queue=new tt(50,{debug:h!=!1});static assignLODInformation(t,e,s,r,o){if(!e)return;e.userData||(e.userData={});const n=new dt(t,s,r,o);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
5
+ `,v),f(null)));if(!O)return f(null);const q=O.parser;s&&console.log("Loading finished "+u,_.guid);let P=0;if(O.parser.json.textures){let v=!1;for(const w of O.parser.json.textures){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){let w=await q.getDependency("texture",P);return w&&m.assignLODInformation(r.url,w,o,e,void 0),s&&console.log('change "'+t.name+'" → "'+w.name+'"',u,P,w,d),t instanceof c.Texture&&(w=this.copySettings(t,w)),w&&(w.guid=_.guid),f(w)}else h&&console.warn("Could not find texture with guid",_.guid,O.parser.json)}if(P=0,O.parser.json.meshes){let v=!1;for(const w of O.parser.json.meshes){if(w?.extensions){const g=w?.extensions[F];if(g?.guid&&g.guid===_.guid){v=!0;break}}P++}if(v){const w=await q.getDependency("mesh",P);if(s&&console.log(`Loaded Mesh "${w.name}"`,u,P,w,d),w.isMesh===!0){const g=w.geometry;return m.assignLODInformation(r.url,g,o,e,0),f(g)}else{const g=new Array;for(let y=0;y<w.children.length;y++){const S=w.children[y];if(S.isMesh===!0){const T=S.geometry;m.assignLODInformation(r.url,T,o,e,y),g.push(T)}}return f(g)}}else h&&console.warn("Could not find mesh with guid",_.guid,O.parser.json)}return f(null)});this.cache.set(d,R),x.use(R);const D=await R;return D!=null?D instanceof c.Texture?(this.cache.set(d,new WeakRef(D)),m._resourceRegistry.register(D,d)):Array.isArray(D)?this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(null)),D}else if(t instanceof c.Texture){s&&console.log("Load texture from uri: "+u);const x=await new c.TextureLoader().loadAsync(u);return x?(x.guid=n.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,s&&console.log(n,x)):h&&console.warn("failed loading",u),x}}return null}static _queue;static get queue(){return this._queue??=new tt(we()?20:50,{debug:h!=!1})}static assignLODInformation(t,e,s,r,o){if(!e)return;e.userData||(e.userData={});const n=new dt(t,s,r,o);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(h==="verbose"&&console.debug(`Copy texture settings
6
6
  `,t.uuid,`
7
- `,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class dt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ue{static addPromise=(t,e,s,r)=>{r.forEach(o=>{o.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let r=this._seen.get(e);if(r>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const k=z("debugprogressive"),ft=z("noprogressive"),ce=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),C={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const s=new M(t,{engine:"unknown",...e});return t[ce]=s,s}renderer;context;projectionScreenMatrix=new c.Matrix4;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ue(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(s);o>=0&&(this._newPromiseGroups.splice(o,1),ve()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new c.Clock;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;re(this.renderer),this.renderer.render=function(s,r){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#i.getDelta(),e.#n+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,k&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let n=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,k&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,k&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of r){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){k&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const d=Math.random()*16777215,x=new c.MeshStandardMaterial({color:d});a.object.material=x}const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let o=s[de];if(o||(o=new ht,s[de]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;n>=0?(C.mesh_lod=n,C.texture_lod=n):(this.calculateLodLevel(e,s,o,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,n),h&&s.material&&!s.isGizmo&&Ie(s.material);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);o.lastLodLevel_Mesh=C.mesh_lod,o.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let r=!1;if((t[U]===void 0||e<t[U])&&(r=!0),s!==void 0&&s>=0&&(r=t[U]!=s,e=s),r){t[U]=e;const o=m.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,o,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[U]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[U]!=r,e=r),s){t[U]=e;const o=t.geometry,n=m.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ue.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,r=t.max,o=(s.x+r.x)*.5,n=(s.y+r.y)*.5;return this._tempPtInside.set(o,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let l=10+1,a=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,d=m.getPrimitiveIndex(e.geometry),x=u&&u.length>0,L=m.getMaterialMinMaxLODsCount(e.material),_=L.min_count!==1/0&&L.min_count>=0&&L.max_count>=0;if(!x&&!_){o.mesh_lod=0,o.texture_lod=0;return}x||(a=!0,l=0);const R=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const p=f[M.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=j(f),B=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=B}}D=f.boundingBox}if(D){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const g=this._tempBox.min,y=this._tempBox.max;let S=g.x,T=g.y,G=y.x,X=y.y;const J=2,oe=1.5,Z=(g.x+y.x)*.5,ee=(g.y+y.y)*.5;S=(S-Z)*J+Z,T=(T-ee)*J+ee,G=(G-Z)*J+Z,X=(X-ee)*J+ee;const Ge=S<0&&G>0?0:Math.min(Math.abs(g.x),Math.abs(y.x)),Fe=T<0&&X>0?0:Math.min(Math.abs(g.y),Math.abs(y.y)),ie=Math.max(Ge,Fe);s.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&R>0&&p.multiplyScalar(R/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,B=this._tempBox2;B.copy(D),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(b);const O=B.getSize(this._tempBox2Size),V=Math.max(O.x,O.y);if(Math.max(p.x,p.y)!=0&&V!=0&&(p.z=O.z/Math.max(O.x,O.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,k&&M.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const y=M.corner0,S=M.corner1,T=M.corner2,G=M.corner3;y.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=y.x,T.copy(this._tempBox.max),T.y=y.y,G.copy(this._tempBox.max);const X=(y.z+G.z)*.5;y.z=S.z=T.z=G.z=X,y.applyMatrix4(g),S.applyMatrix4(g),T.applyMatrix4(g),G.applyMatrix4(g),M.debugDrawLine(y,S,255),M.debugDrawLine(y,T,255),M.debugDrawLine(S,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let g=0;g<u.length;g++){const y=u[g],T=(y.densities?.[d]||y.density||1e-5)/s.lastScreenCoverage;if(d>0&&ve()&&!y.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T<r){v=g;break}}v<l&&(l=v,a=!0)}if(a?o.mesh_lod=l:o.mesh_lod=s.lastLodLevel_Mesh,k&&o.mesh_lod!=s.lastLodLevel_Mesh){const p=u?.[o.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${p.densities?.[d].toFixed(0)}) | ${e.name}`)}if(_){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(o.texture_lod=L.max_count-1,k){const p=L.lods[L.max_count-1];k&&console.log(`First Texture LOD ${o.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const O=R/window.devicePixelRatio*b;let V=!1;for(let P=L.lods.length-1;P>=0;P--){const v=L.lods[P];if(!(f&&v.max_height>=2048)&&!(Ae()&&v.max_height>4096)&&(v.max_height>O||!V&&P===0)){if(V=!0,o.texture_lod=P,k&&o.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${w}px
7
+ `,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class dt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ue{static addPromise=(t,e,s,r)=>{r.forEach(o=>{o.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){h&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let r=this._seen.get(e);if(r>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const k=z("debugprogressive"),ft=z("noprogressive"),ce=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),C={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const s=new M(t,{engine:"unknown",...e});return t[ce]=s,s}renderer;context;projectionScreenMatrix=new c.Matrix4;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ue(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(s);o>=0&&(this._newPromiseGroups.splice(o,1),Me()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new c.Clock;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;re(this.renderer),this.renderer.render=function(s,r){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#i.getDelta(),e.#n+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,k&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let n=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(ft||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,k&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,k&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of r){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){k&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const d=Math.random()*16777215,x=new c.MeshStandardMaterial({color:d});a.object.material=x}const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,o)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let o=s[de];if(o||(o=new ht,s[de]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;n>=0?(C.mesh_lod=n,C.texture_lod=n):(this.calculateLodLevel(e,s,o,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,n),h&&s.material&&!s.isGizmo&&Ie(s.material);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);o.lastLodLevel_Mesh=C.mesh_lod,o.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let r=!1;if((t[U]===void 0||e<t[U])&&(r=!0),s!==void 0&&s>=0&&(r=t[U]!=s,e=s),r){t[U]=e;const o=m.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,o,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[U]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[U]!=r,e=r),s){t[U]=e;const o=t.geometry,n=m.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ue.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,r=t.max,o=(s.x+r.x)*.5,n=(s.y+r.y)*.5;return this._tempPtInside.set(o,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let l=10+1,a=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,d=m.getPrimitiveIndex(e.geometry),x=u&&u.length>0,L=m.getMaterialMinMaxLODsCount(e.material),_=L.min_count!==1/0&&L.min_count>=0&&L.max_count>=0;if(!x&&!_){o.mesh_lod=0,o.texture_lod=0;return}x||(a=!0,l=0);const R=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const p=f[M.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=j(f),B=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=B}}D=f.boundingBox}if(D){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const g=this._tempBox.min,y=this._tempBox.max;let S=g.x,T=g.y,G=y.x,X=y.y;const J=2,oe=1.5,Z=(g.x+y.x)*.5,ee=(g.y+y.y)*.5;S=(S-Z)*J+Z,T=(T-ee)*J+ee,G=(G-Z)*J+Z,X=(X-ee)*J+ee;const Ge=S<0&&G>0?0:Math.min(Math.abs(g.x),Math.abs(y.x)),Fe=T<0&&X>0?0:Math.min(Math.abs(g.y),Math.abs(y.y)),ie=Math.max(Ge,Fe);s.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&R>0&&p.multiplyScalar(R/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,B=this._tempBox2;B.copy(D),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(b);const O=B.getSize(this._tempBox2Size),q=Math.max(O.x,O.y);if(Math.max(p.x,p.y)!=0&&q!=0&&(p.z=O.z/Math.max(O.x,O.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,k&&M.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const y=M.corner0,S=M.corner1,T=M.corner2,G=M.corner3;y.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=y.x,T.copy(this._tempBox.max),T.y=y.y,G.copy(this._tempBox.max);const X=(y.z+G.z)*.5;y.z=S.z=T.z=G.z=X,y.applyMatrix4(g),S.applyMatrix4(g),T.applyMatrix4(g),G.applyMatrix4(g),M.debugDrawLine(y,S,255),M.debugDrawLine(y,T,255),M.debugDrawLine(S,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let g=0;g<u.length;g++){const y=u[g],T=(y.densities?.[d]||y.density||1e-5)/s.lastScreenCoverage;if(d>0&&Me()&&!y.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T<r){v=g;break}}v<l&&(l=v,a=!0)}if(a?o.mesh_lod=l:o.mesh_lod=s.lastLodLevel_Mesh,k&&o.mesh_lod!=s.lastLodLevel_Mesh){const p=u?.[o.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${p.densities?.[d].toFixed(0)}) | ${e.name}`)}if(_){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(o.texture_lod=L.max_count-1,k){const p=L.lods[L.max_count-1];k&&console.log(`First Texture LOD ${o.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const O=R/window.devicePixelRatio*b;let q=!1;for(let P=L.lods.length-1;P>=0;P--){const v=L.lods[P];if(!(f&&v.max_height>=2048)&&!(we()&&v.max_height>4096)&&(v.max_height>O||!q&&P===0)){if(q=!0,o.texture_lod=P,k&&o.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${w}px
8
8
  Screensize: ${O.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${p.toFixed(1)}
9
- ${e.name}`)}break}}}}else o.texture_lod=0}}class ht{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const Me=Symbol("NEEDLE_mesh_lod"),te=Symbol("NEEDLE_texture_lod");let fe=null;function Le(){const i=gt();i&&(i.mapURLs(function(t){return be(),t}),be(),fe?.disconnect(),fe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),fe.observe(document,{childList:!0,subtree:!0}))}function gt(){if(typeof customElements>"u")return null;const i=customElements.get("model-viewer");return i||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Le()}),null)}function be(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{$e(t)})}const De=new WeakSet;let pt=0;function $e(i){if(!i||De.has(i))return null;De.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++pt+`
10
- `,i.getAttribute("src"));let t=null,e=null,s=null;for(let r=i;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),n=o.find(u=>u.toString()=="Symbol(renderer)"),l=o.find(u=>u.toString()=="Symbol(scene)"),a=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let r=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=M.get(t,{engine:"model-viewer"});return M.addPlugin(new mt),o.enable(),o.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class mt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[te]==!0)return;e[te]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(o&&s&&e.material){let n=function(a){if(a[te]==!0)return;a[te]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let d=0;d<u.length;d++){const x=u[d],L=a[x];if(L?.isTexture===!0){const _=L.userData?.associations?.textures;if(_==null)continue;const R=s.parser.json.textures[_];if(!R){console.warn("Texture data not found for texture index "+_);continue}if(R?.extensions?.[F]){const D=R.extensions[F];D&&o&&m.registerTexture(o,L,D.lods.length,_,D)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Me]==!0)return;e[Me]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const o=e.userData?.gltfExtensions?.[F];if(o&&r){const n=e.uuid;m.registerMesh(r,n,e,0,o.lods.length,o)}}}function Be(...i){let t,e,s,r;switch(i.length){case 2:[s,e]=i,r={};break;case 3:[s,e,r]=i;break;case 4:[t,e,s,r]=i;break;default:throw new Error("Invalid arguments")}re(e),ye(s),xe(s,{progressive:!0,...r?.hints}),s.register(n=>new m(n));const o=M.get(e);return r?.enableLODsManager!==!1&&o.enable(),o}Le();if(!ot){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:M,configureLoader:xe,getRaycastMesh:j,useRaycastMeshes:ke}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.EXTENSION_NAME=F;exports.LODsManager=M;exports.NEEDLE_progressive=m;exports.VERSION=Oe;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=re;exports.getRaycastMesh=j;exports.patchModelViewer=Le;exports.registerRaycastMesh=Re;exports.setDracoDecoderLocation=Te;exports.setKTX2TranscoderLocation=Pe;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=ke;
9
+ ${e.name}`)}break}}}}else o.texture_lod=0}}class ht{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const be=Symbol("NEEDLE_mesh_lod"),te=Symbol("NEEDLE_texture_lod");let fe=null;function _e(){const i=gt();i&&(i.mapURLs(function(t){return De(),t}),De(),fe?.disconnect(),fe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&$e(s)})})}),fe.observe(document,{childList:!0,subtree:!0}))}function gt(){if(typeof customElements>"u")return null;const i=customElements.get("model-viewer");return i||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),_e()}),null)}function De(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{$e(t)})}const Oe=new WeakSet;let pt=0;function $e(i){if(!i||Oe.has(i))return null;Oe.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++pt+`
10
+ `,i.getAttribute("src"));let t=null,e=null,s=null;for(let r=i;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),n=o.find(u=>u.toString()=="Symbol(renderer)"),l=o.find(u=>u.toString()=="Symbol(scene)"),a=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let r=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=M.get(t,{engine:"model-viewer"});return M.addPlugin(new mt),o.enable(),o.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class mt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[te]==!0)return;e[te]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(o&&s&&e.material){let n=function(a){if(a[te]==!0)return;a[te]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let d=0;d<u.length;d++){const x=u[d],L=a[x];if(L?.isTexture===!0){const _=L.userData?.associations?.textures;if(_==null)continue;const R=s.parser.json.textures[_];if(!R){console.warn("Texture data not found for texture index "+_);continue}if(R?.extensions?.[F]){const D=R.extensions[F];D&&o&&m.registerTexture(o,L,D.lods.length,_,D)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[be]==!0)return;e[be]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const o=e.userData?.gltfExtensions?.[F];if(o&&r){const n=e.uuid;m.registerMesh(r,n,e,0,o.lods.length,o)}}}function Be(...i){let t,e,s,r;switch(i.length){case 2:[s,e]=i,r={};break;case 3:[s,e,r]=i;break;case 4:[t,e,s,r]=i;break;default:throw new Error("Invalid arguments")}re(e),ye(s),xe(s,{progressive:!0,...r?.hints}),s.register(n=>new m(n));const o=M.get(e);return r?.enableLODsManager!==!1&&o.enable(),o}_e();if(!ot){const i={gltfProgressive:{useNeedleProgressive:Be,LODsManager:M,configureLoader:xe,getRaycastMesh:j,useRaycastMeshes:ke}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.EXTENSION_NAME=F;exports.LODsManager=M;exports.NEEDLE_progressive=m;exports.VERSION=Se;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=re;exports.getRaycastMesh=j;exports.patchModelViewer=_e;exports.registerRaycastMesh=Re;exports.setDracoDecoderLocation=Pe;exports.setKTX2TranscoderLocation=Ce;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=ke;
@@ -78,7 +78,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
78
78
  static assignTextureLOD(materialOrTexture: Texture, level: number): Promise<Texture | null>;
79
79
  /**
80
80
  * Set the maximum number of concurrent loading tasks for LOD resources. This limits how many LOD resources (meshes or textures) can be loaded at the same time to prevent overloading the network or GPU. If the limit is reached, additional loading requests will be queued and processed as previous ones finish.
81
- * @default 50
81
+ * @default 50 on desktop, 20 on mobile devices
82
82
  */
83
83
  static set maxConcurrentLoadingTasks(value: number);
84
84
  static get maxConcurrentLoadingTasks(): number;
@@ -134,7 +134,8 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
134
134
  private static readonly workers;
135
135
  private static _workersIndex;
136
136
  private static getOrLoadLOD;
137
- private static queue;
137
+ private static _queue;
138
+ private static get queue();
138
139
  private static assignLODInformation;
139
140
  private static getAssignedLODInformation;
140
141
  private static copySettings;
package/lib/extension.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BufferGeometry, Mesh, Texture, TextureLoader } from "three";
2
2
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { addDracoAndKTX2Loaders } from "./loaders.js";
4
- import { determineTextureMemoryInBytes, getParam, PromiseQueue, resolveUrl } from "./utils.internal.js";
4
+ import { determineTextureMemoryInBytes, getParam, isMobileDevice, PromiseQueue, resolveUrl } from "./utils.internal.js";
5
5
  import { getRaycastMesh, registerRaycastMesh } from "./utils.js";
6
6
  // All of this has to be removed
7
7
  // import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
@@ -312,7 +312,7 @@ export class NEEDLE_progressive {
312
312
  }
313
313
  /**
314
314
  * Set the maximum number of concurrent loading tasks for LOD resources. This limits how many LOD resources (meshes or textures) can be loaded at the same time to prevent overloading the network or GPU. If the limit is reached, additional loading requests will be queued and processed as previous ones finish.
315
- * @default 50
315
+ * @default 50 on desktop, 20 on mobile devices
316
316
  */
317
317
  static set maxConcurrentLoadingTasks(value) {
318
318
  NEEDLE_progressive.queue.maxConcurrent = value;
@@ -1056,7 +1056,8 @@ export class NEEDLE_progressive {
1056
1056
  }
1057
1057
  return null;
1058
1058
  }
1059
- static queue = new PromiseQueue(50, { debug: debug != false });
1059
+ static _queue;
1060
+ static get queue() { return this._queue ??= new PromiseQueue(isMobileDevice() ? 20 : 50, { debug: debug != false }); }
1060
1061
  static assignLODInformation(url, res, key, level, index) {
1061
1062
  if (!res)
1062
1063
  return;
@@ -74,7 +74,8 @@ export class PromiseQueue {
74
74
  constructor(maxConcurrent, opts = {}) {
75
75
  this.maxConcurrent = maxConcurrent;
76
76
  this.debug = opts.debug ?? false;
77
- window.requestAnimationFrame(this.tick);
77
+ if (typeof window !== "undefined")
78
+ window.requestAnimationFrame(this.tick);
78
79
  }
79
80
  tick = () => {
80
81
  this.internalUpdate();
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // replaced at build time
2
- export const version = "3.4.0-beta.2";
2
+ export const version = "3.4.0-beta.3";
3
3
  globalThis["GLTF_PROGRESSIVE_VERSION"] = version;
4
4
  console.debug(`[gltf-progressive] version ${version || "-"}`);
@@ -21,7 +21,7 @@ export function getWorker(opts) {
21
21
  class GLTFLoaderWorker {
22
22
  worker;
23
23
  static async createWorker(opts) {
24
- const worker = new Worker(new URL(`./loader.worker.js`, import.meta.url), {
24
+ const worker = new Worker(new URL(`./gltf-progressive.worker.js`, import.meta.url), {
25
25
  type: 'module',
26
26
  });
27
27
  const instance = new GLTFLoaderWorker(worker, opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-progressive",
3
- "version": "3.4.0-beta.2",
3
+ "version": "3.4.0-canary.4c8f053",
4
4
  "description": "three.js support for loading glTF or GLB files that contain progressive loading data",
5
5
  "homepage": "https://needle.tools",
6
6
  "author": {
@@ -73,4 +73,4 @@
73
73
  "vite": "7"
74
74
  },
75
75
  "types": "./lib/index.d.ts"
76
- }
76
+ }