@needle-tools/gltf-progressive 1.0.0-alpha.4 → 1.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,8 @@ 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
+ ## [1.0.0-alpha.6] - 2023-05-01
8
+ - fix: LOD mesh assignment for multi-material meshes (meshes with multiple primitives)
7
9
 
8
- ## [1.0.0-alpha.2] - 2023-04-29
10
+ ## [1.0.0-alpha.5] - 2023-04-30
9
11
  - initial version
@@ -6,7 +6,8 @@ import { GLTFLoader as ge } from "three/examples/jsm/loaders/GLTFLoader.js";
6
6
  import { MeshoptDecoder as he } from "three/examples/jsm/libs/meshopt_decoder.module.js";
7
7
  import { DRACOLoader as pe } from "three/examples/jsm/loaders/DRACOLoader.js";
8
8
  import { KTX2Loader as ye } from "three/examples/jsm/loaders/KTX2Loader.js";
9
- let Le = "https://www.gstatic.com/draco/versioned/decoders/1.4.1/", De = "https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/", U, V, I;
9
+ const Le = "https://www.gstatic.com/draco/versioned/decoders/1.4.1/", De = "https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";
10
+ let U, V, I;
10
11
  function se(l) {
11
12
  U || (U = new pe(), U.setDecoderPath(Le), U.setDecoderConfig({ type: "js" })), I || (I = new ye(), I.setTranscoderPath(De)), V || (V = he), l ? I.detectSupport(l) : console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail");
12
13
  }
@@ -14,7 +15,7 @@ function ie(l) {
14
15
  l.dracoLoader || l.setDRACOLoader(U), l.ktx2Loader || l.setKTX2Loader(I), l.meshoptDecoder || l.setMeshoptDecoder(V);
15
16
  }
16
17
  function Q(l) {
17
- let t = new URL(window.location.href).searchParams.get(l);
18
+ const t = new URL(window.location.href).searchParams.get(l);
18
19
  return t == null || t === "0" || t === "false" ? !1 : t === "" ? !0 : t;
19
20
  }
20
21
  function me(l, e) {
@@ -40,8 +41,8 @@ if (S) {
40
41
  for (const s of i.keys) {
41
42
  const n = o[s];
42
43
  if (n.isBufferGeometry === !0) {
43
- const a = M.getMeshLODInformation(n), d = a ? Math.min(e, a.lods.length) : 0;
44
- o["DEBUG:LOD"] = d, M.assignMeshLOD(o, d), a && (t = Math.max(t, a.lods.length - 1));
44
+ const a = M.getMeshLODInformation(n), g = a ? Math.min(e, a.lods.length) : 0;
45
+ o["DEBUG:LOD"] = e, M.assignMeshLOD(o, g), a && (t = Math.max(t, a.lods.length - 1));
45
46
  } else if (o.isMaterial === !0) {
46
47
  o["DEBUG:LOD"] = e, M.assignTextureLOD(o, e);
47
48
  break;
@@ -175,8 +176,8 @@ const v = class {
175
176
  return Promise.all(i).then((s) => {
176
177
  const n = new Array();
177
178
  for (let a = 0; a < s.length; a++) {
178
- const d = s[a], f = o[a];
179
- d && d.isTexture === !0 ? n.push({ material: r, slot: f, texture: d, level: t }) : n.push({ material: r, slot: f, texture: null, level: t });
179
+ const g = s[a], f = o[a];
180
+ g && g.isTexture === !0 ? n.push({ material: r, slot: f, texture: g, level: t }) : n.push({ material: r, slot: f, texture: null, level: t });
180
181
  }
181
182
  return n;
182
183
  });
@@ -223,14 +224,14 @@ const v = class {
223
224
  for (const n of this.parser.associations.keys())
224
225
  if (n.isMesh) {
225
226
  const a = this.parser.associations.get(n);
226
- a.meshes === o && v.registerMesh(this.url, n.uuid, n, s.lods.length, a.primitives, s);
227
+ a.meshes === o && v.registerMesh(this.url, s.guid, n, s.lods.length, a.primitives, s);
227
228
  }
228
229
  }
229
230
  }
230
231
  }), null;
231
232
  }
232
233
  static async getOrLoadLOD(e, t) {
233
- var n, a, d;
234
+ var n, a, g;
234
235
  const r = S == "verbose", i = e.userData.LODS;
235
236
  if (!i)
236
237
  return null;
@@ -242,9 +243,9 @@ const v = class {
242
243
  }
243
244
  if (s || (s = v.lodInfos.get(o)), s) {
244
245
  if (t > 0) {
245
- let g = !1;
246
+ let d = !1;
246
247
  const w = Array.isArray(s.lods);
247
- if (w && t >= s.lods.length ? g = !0 : w || (g = !0), g)
248
+ if (w && t >= s.lods.length ? d = !0 : w || (d = !0), d)
248
249
  return this.lowresCache.get(o);
249
250
  }
250
251
  const f = Array.isArray(s.lods) ? s.lods[t].path : s.lods;
@@ -254,23 +255,23 @@ const v = class {
254
255
  if (L.endsWith(".glb") || L.endsWith(".gltf")) {
255
256
  if (!s.guid)
256
257
  return console.warn("missing pointer for glb/gltf texture", s), null;
257
- const g = L + "_" + s.guid, w = this.previouslyLoaded.get(g);
258
+ const d = L + "_" + s.guid, w = this.previouslyLoaded.get(d);
258
259
  if (w !== void 0) {
259
- r && console.log(`LOD ${t} was already loading/loaded: ${g}`);
260
- let m = await w.catch((E) => (console.error(`Error loading LOD ${t} from ${L}
261
- `, E), null)), B = !1;
262
- if (m == null || (m instanceof z && e instanceof z ? (n = m.image) != null && n.data || (a = m.source) != null && a.data ? m = this.copySettings(e, m) : (B = !0, this.previouslyLoaded.delete(g)) : m instanceof X && e instanceof X && ((d = m.attributes.position) != null && d.array || (B = !0, this.previouslyLoaded.delete(g)))), !B)
260
+ r && console.log(`LOD ${t} was already loading/loaded: ${d}`);
261
+ let m = await w.catch((G) => (console.error(`Error loading LOD ${t} from ${L}
262
+ `, G), null)), B = !1;
263
+ if (m == null || (m instanceof z && e instanceof z ? (n = m.image) != null && n.data || (a = m.source) != null && a.data ? m = this.copySettings(e, m) : (B = !0, this.previouslyLoaded.delete(d)) : m instanceof X && e instanceof X && ((g = m.attributes.position) != null && g.array || (B = !0, this.previouslyLoaded.delete(d)))), !B)
263
264
  return m;
264
265
  }
265
- const D = s, P = new Promise(async (m, B) => {
266
- const E = new ge();
267
- ie(E), S && (await new Promise((p) => setTimeout(p, 1e3)), r && console.warn("Start loading (delayed) " + L, D.guid));
266
+ const D = s, A = new Promise(async (m, B) => {
267
+ const G = new ge();
268
+ ie(G), S && (await new Promise((p) => setTimeout(p, 1e3)), r && console.warn("Start loading (delayed) " + L, D.guid));
268
269
  let y = L;
269
270
  if (D && Array.isArray(D.lods)) {
270
271
  const p = D.lods[t];
271
272
  p.hash && (y += "?v=" + p.hash);
272
273
  }
273
- const h = await E.loadAsync(y).catch((p) => (console.error(`Error loading LOD ${t} from ${L}
274
+ const h = await G.loadAsync(y).catch((p) => (console.error(`Error loading LOD ${t} from ${L}
274
275
  `, p), null));
275
276
  if (!h)
276
277
  return null;
@@ -291,7 +292,7 @@ const v = class {
291
292
  }
292
293
  if (p) {
293
294
  let c = await T.getDependency("texture", x);
294
- return r && console.log('change "' + e.name + '" → "' + c.name + '"', L, x, c, g), e instanceof z && (c = this.copySettings(e, c)), c && (c.guid = D.guid), m(c);
295
+ return r && console.log('change "' + e.name + '" → "' + c.name + '"', L, x, c, d), e instanceof z && (c = this.copySettings(e, c)), c && (c.guid = D.guid), m(c);
295
296
  }
296
297
  }
297
298
  if (x = 0, h.parser.json.meshes) {
@@ -308,7 +309,7 @@ const v = class {
308
309
  }
309
310
  if (p) {
310
311
  const c = await T.getDependency("mesh", x), O = D;
311
- if (r && console.log(`Loaded Mesh "${c.name}"`, L, x, c, g), c.isMesh === !0) {
312
+ if (r && console.log(`Loaded Mesh "${c.name}"`, L, x, c, d), c.isMesh === !0) {
312
313
  const b = c.geometry;
313
314
  return v.assignLODInformation(i.url, b, o, t, void 0, O.density), m(b);
314
315
  } else {
@@ -326,7 +327,7 @@ const v = class {
326
327
  }
327
328
  return m(null);
328
329
  });
329
- return this.previouslyLoaded.set(g, P), await P;
330
+ return this.previouslyLoaded.set(d, A), await A;
330
331
  } else if (e instanceof z) {
331
332
  r && console.log("Load texture from uri: " + L);
332
333
  const w = await new ce().loadAsync(L);
@@ -358,20 +359,20 @@ let M = v;
358
359
  */
359
360
  u(M, "registerTexture", (e, t, r, i) => {
360
361
  S && console.log("> Progressive: register texture", r, t.name, t.uuid, t, i), t.source && (t.source[q] = i);
361
- const o = t.uuid;
362
+ const o = i.guid;
362
363
  v.assignLODInformation(e, t, o, 0, 0, void 0), v.lodInfos.set(o, i), v.lowresCache.set(o, t);
363
364
  }), /**
364
365
  * Register a mesh with LOD information
365
366
  */
366
367
  u(M, "registerMesh", (e, t, r, i, o, s) => {
367
- var d;
368
+ var g;
368
369
  S && console.log("> Progressive: register mesh", o, r.name, s, r.uuid, r);
369
370
  const n = r.geometry;
370
371
  n.userData || (n.userData = {}), v.assignLODInformation(e, n, t, i, o, s.density), v.lodInfos.set(t, s);
371
372
  let a = v.lowresCache.get(t);
372
373
  a ? a.push(r.geometry) : a = [r.geometry], v.lowresCache.set(t, a);
373
374
  for (const f of H)
374
- (d = f.onRegisteredNewMesh) == null || d.call(f, r, s);
375
+ (g = f.onRegisteredNewMesh) == null || g.call(f, r, s);
375
376
  }), /** A map of key = asset uuid and value = LOD information */
376
377
  u(M, "lodInfos", /* @__PURE__ */ new Map()), /** cache of already loaded mesh lods */
377
378
  u(M, "previouslyLoaded", /* @__PURE__ */ new Map()), /** this contains the geometry/textures that were originally loaded */
@@ -389,13 +390,18 @@ class xe {
389
390
  this.url = e, this.key = t, this.level = r, i != null && (this.index = i), o != null && (this.density = o);
390
391
  }
391
392
  }
392
- let te = Q("debugprogressive");
393
- const we = Q("noprogressive"), A = class {
393
+ const te = Q("debugprogressive"), we = Q("noprogressive"), P = class {
394
394
  constructor(e) {
395
395
  u(this, "renderer");
396
396
  u(this, "projectionScreenMatrix", new Z());
397
397
  u(this, "cameraFrustrum", new fe());
398
+ /**
399
+ * The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 1, the LODs will be updated every second frame, etc.
400
+ */
398
401
  u(this, "updateInterval", 0);
402
+ /**
403
+ * If set to true, the LODsManager will not update the LODs.
404
+ */
399
405
  u(this, "pause", !1);
400
406
  u(this, "plugins", []);
401
407
  u(this, "_originalRender");
@@ -408,6 +414,14 @@ const we = Q("noprogressive"), A = class {
408
414
  u(this, "_tempBox2Size", new C());
409
415
  this.renderer = e;
410
416
  }
417
+ /** @internal */
418
+ static getObjectLODState(e) {
419
+ var t;
420
+ return (t = e.userData) == null ? void 0 : t.LOD_state;
421
+ }
422
+ /**
423
+ * Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
424
+ */
411
425
  enable() {
412
426
  if (this._originalRender)
413
427
  return;
@@ -431,17 +445,13 @@ const we = Q("noprogressive"), A = class {
431
445
  this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix, t.matrixWorldInverse), this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
432
446
  const o = 1e5, n = this.renderer.renderLists.get(e, r).opaque;
433
447
  for (const a of n) {
434
- const d = a.object;
435
- (d instanceof Y || d.isMesh) && this.updateLODs(e, t, d, o);
448
+ const g = a.object;
449
+ (g instanceof Y || g.isMesh) && this.updateLODs(e, t, g, o);
436
450
  }
437
451
  }
438
- static getObjectLODState(e) {
439
- var t;
440
- return (t = e.userData) == null ? void 0 : t.LOD_state;
441
- }
442
452
  /** Update the LOD levels for the renderer. */
443
453
  updateLODs(e, t, r, i) {
444
- var a, d;
454
+ var a, g;
445
455
  for (const f of this.plugins)
446
456
  (a = f.onBeforeUpdateLOD) == null || a.call(f, this.renderer, e, t, r);
447
457
  let o = r.userData.LOD_state;
@@ -458,7 +468,7 @@ const we = Q("noprogressive"), A = class {
458
468
  this.loadProgressiveTextures(r.material, n);
459
469
  }
460
470
  for (const f of this.plugins)
461
- (d = f.onAfterUpdatedLOD) == null || d.call(f, this.renderer, e, t, r, s);
471
+ (g = f.onAfterUpdatedLOD) == null || g.call(f, this.renderer, e, t, r, s);
462
472
  o.lastLodLevel = s;
463
473
  }
464
474
  /** Load progressive textures for the given material
@@ -493,8 +503,8 @@ const we = Q("noprogressive"), A = class {
493
503
  if (e) {
494
504
  if (te && t["DEBUG:LOD"] != null)
495
505
  return t["DEBUG:LOD"];
496
- const a = M.getMeshLODInformation(t.geometry), d = a == null ? void 0 : a.lods;
497
- if (!d || d.length <= 0 || !((n = this.cameraFrustrum) != null && n.intersectsObject(t)))
506
+ const a = M.getMeshLODInformation(t.geometry), g = a == null ? void 0 : a.lods;
507
+ if (!g || g.length <= 0 || !((n = this.cameraFrustrum) != null && n.intersectsObject(t)))
498
508
  return 99;
499
509
  const f = t.geometry.boundingBox;
500
510
  if (f && e.isPerspectiveCamera) {
@@ -514,23 +524,23 @@ const we = Q("noprogressive"), A = class {
514
524
  r.lastCentrality = (b - N) * (b - N) * (b - N);
515
525
  } else
516
526
  r.lastCentrality = 1;
517
- const g = this._tempBox.getSize(this._tempBoxSize);
518
- g.multiplyScalar(0.5), screen.availHeight > 0 && g.multiplyScalar(this.renderer.domElement.clientHeight / screen.availHeight), g.x *= L.aspect;
527
+ const d = this._tempBox.getSize(this._tempBoxSize);
528
+ d.multiplyScalar(0.5), screen.availHeight > 0 && d.multiplyScalar(this.renderer.domElement.clientHeight / screen.availHeight), d.x *= L.aspect;
519
529
  const w = e.matrixWorldInverse, D = new j();
520
530
  D.copy(f), D.applyMatrix4(t.matrixWorld), D.applyMatrix4(w);
521
- const P = D.getSize(this._tempBox2Size), G = Math.max(P.x, P.y);
522
- if (Math.max(g.x, g.y) != 0 && G != 0 && (g.z = P.z / Math.max(P.x, P.y) * Math.max(g.x, g.y)), r.lastScreenCoverage = Math.max(g.x, g.y, g.z), r.lastScreenspaceVolume.copy(g), r.lastScreenCoverage *= r.lastCentrality, te && A.debugDrawLine) {
531
+ const A = D.getSize(this._tempBox2Size), E = Math.max(A.x, A.y);
532
+ if (Math.max(d.x, d.y) != 0 && E != 0 && (d.z = A.z / Math.max(A.x, A.y) * Math.max(d.x, d.y)), r.lastScreenCoverage = Math.max(d.x, d.y, d.z), r.lastScreenspaceVolume.copy(d), r.lastScreenCoverage *= r.lastCentrality, te && P.debugDrawLine) {
523
533
  const y = this.tempMatrix.copy(this.projectionScreenMatrix);
524
534
  y.invert();
525
- const h = A.corner0, T = A.corner1, x = A.corner2, p = A.corner3;
535
+ const h = P.corner0, T = P.corner1, x = P.corner2, p = P.corner3;
526
536
  h.copy(this._tempBox.min), T.copy(this._tempBox.max), T.x = h.x, x.copy(this._tempBox.max), x.y = h.y, p.copy(this._tempBox.max);
527
537
  const c = (h.z + p.z) * 0.5;
528
- h.z = T.z = x.z = p.z = c, h.applyMatrix4(y), T.applyMatrix4(y), x.applyMatrix4(y), p.applyMatrix4(y), A.debugDrawLine(h, T, 255), A.debugDrawLine(h, x, 255), A.debugDrawLine(T, p, 255), A.debugDrawLine(x, p, 255);
538
+ h.z = T.z = x.z = p.z = c, h.applyMatrix4(y), T.applyMatrix4(y), x.applyMatrix4(y), p.applyMatrix4(y), P.debugDrawLine(h, T, 255), P.debugDrawLine(h, x, 255), P.debugDrawLine(T, p, 255), P.debugDrawLine(x, p, 255);
529
539
  }
530
540
  let B = 999;
531
- if (d && r.lastScreenCoverage > 0) {
532
- for (let y = 0; y < d.length; y++)
533
- if (d[y].density / r.lastScreenCoverage < i) {
541
+ if (g && r.lastScreenCoverage > 0) {
542
+ for (let y = 0; y < g.length; y++)
543
+ if (g[y].density / r.lastScreenCoverage < i) {
534
544
  B = y;
535
545
  break;
536
546
  }
@@ -541,8 +551,10 @@ const we = Q("noprogressive"), A = class {
541
551
  return s;
542
552
  }
543
553
  };
544
- let k = A;
545
- u(k, "corner0", new C()), u(k, "corner1", new C()), u(k, "corner2", new C()), u(k, "corner3", new C()), u(k, "debugDrawLine");
554
+ let k = P;
555
+ /** 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.
556
+ */
557
+ u(k, "debugDrawLine"), u(k, "corner0", new C()), u(k, "corner1", new C()), u(k, "corner2", new C()), u(k, "corner3", new C());
546
558
  class Oe {
547
559
  constructor() {
548
560
  u(this, "lastLodLevel", 0);
@@ -552,20 +564,26 @@ class Oe {
552
564
  }
553
565
  }
554
566
  const re = Symbol("NEEDLE_mesh_lod"), K = Symbol("NEEDLE_texture_lod");
555
- function ve() {
556
- const l = document.querySelector("model-viewer");
567
+ function ve(l) {
557
568
  if (!l)
558
- return;
559
- let e = null;
560
- for (let t = l; t != null; t = Object.getPrototypeOf(t)) {
561
- const i = Object.getOwnPropertySymbols(t).find((o) => o.toString() == "Symbol(renderer)");
562
- !e && i != null && (e = l[i].threeRenderer);
569
+ return null;
570
+ let e = null, t = null;
571
+ for (let r = l; r != null; r = Object.getPrototypeOf(r)) {
572
+ const i = Object.getOwnPropertySymbols(r), o = i.find((n) => n.toString() == "Symbol(renderer)"), s = i.find((n) => n.toString() == "Symbol(scene)");
573
+ !e && o != null && (e = l[o].threeRenderer), !t && s != null && (t = l[s]);
563
574
  }
564
575
  if (e) {
565
576
  console.log("Adding Needle LODs to modelviewer");
566
- const t = new k(e);
567
- t.plugins.push(new Me(l)), t.enable();
577
+ const r = new k(e);
578
+ if (r.plugins.push(new Me(l)), r.enable(), t) {
579
+ const i = t.camera || t.traverse((o) => o.type == "PerspectiveCamera")[0];
580
+ i && e.render(t, i);
581
+ }
582
+ return () => {
583
+ r.disable();
584
+ };
568
585
  }
586
+ return null;
569
587
  }
570
588
  class Me {
571
589
  constructor(e) {
@@ -592,17 +610,17 @@ class Me {
592
610
  }
593
611
  if (r && t.material) {
594
612
  let o = function(n) {
595
- var d, f, L;
613
+ var g, f, L;
596
614
  if (n[K] == !0)
597
615
  return;
598
616
  n[K] = !0, n.userData && (n.userData.LOD = -1);
599
617
  const a = Object.keys(n);
600
- for (let g = 0; g < a.length; g++) {
601
- const w = a[g], D = n[w];
618
+ for (let d = 0; d < a.length; d++) {
619
+ const w = a[d], D = n[w];
602
620
  if ((D == null ? void 0 : D.isTexture) === !0) {
603
- const P = (f = (d = D.userData) == null ? void 0 : d.associations) == null ? void 0 : f.textures, G = r.parser.json.textures[P];
604
- if ((L = G.extensions) != null && L[R]) {
605
- const m = G.extensions[R];
621
+ const A = (f = (g = D.userData) == null ? void 0 : g.associations) == null ? void 0 : f.textures, E = r.parser.json.textures[A];
622
+ if ((L = E.extensions) != null && L[R]) {
623
+ const m = E.extensions[R];
606
624
  m && i && M.registerTexture(i, D, m.lods.length, m);
607
625
  }
608
626
  }
@@ -633,13 +651,13 @@ class Me {
633
651
  }
634
652
  }
635
653
  }
636
- function Be(l, e, t) {
637
- se(e), ie(t), t.register((i) => new M(i, l));
638
- const r = new k(e);
639
- return r.enable(), r;
654
+ function Be(l, e, t, r) {
655
+ se(e), ie(t), t.register((o) => new M(o, l));
656
+ const i = new k(e);
657
+ return (r == null ? void 0 : r.enableLODsManager) !== !1 && i.enable(), i;
640
658
  }
641
659
  document.addEventListener("DOMContentLoaded", () => {
642
- ve();
660
+ ve(document.querySelector("model-viewer"));
643
661
  });
644
662
  export {
645
663
  R as EXTENSION_NAME,
@@ -647,5 +665,5 @@ export {
647
665
  M as NEEDLE_progressive,
648
666
  ve as patchModelViewer,
649
667
  ke as registerPlugin,
650
- Be as useNeedleGLTFProgressive
668
+ Be as useNeedleProgressive
651
669
  };
@@ -0,0 +1,3 @@
1
+ var se=Object.defineProperty,ne=(r,e,t)=>e in r?se(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,u=(r,e,t)=>(ne(r,typeof e!="symbol"?e+"":e,t),t);import{Mesh as W,BufferGeometry as U,Material as oe,RawShaderMaterial as ie,Texture as C,TextureLoader as ae,Matrix4 as K,Frustum as le,Sphere as ue,Box3 as Y,Vector3 as E}from"three";import{GLTFLoader as ce}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as de}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as he}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as fe}from"three/examples/jsm/loaders/KTX2Loader.js";const ge="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",pe="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";let R,$,G;function J(r){R||(R=new he,R.setDecoderPath(ge),R.setDecoderConfig({type:"js"})),G||(G=new fe,G.setTranscoderPath(pe)),$||($=de),r?G.detectSupport(r):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Q(r){r.dracoLoader||r.setDRACOLoader(R),r.ktx2Loader||r.setKTX2Loader(G),r.meshoptDecoder||r.setMeshoptDecoder($)}function q(r){const e=new URL(window.location.href).searchParams.get(r);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function me(r,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||r===void 0)return e;const t=r.lastIndexOf("/");if(t>=0){const n=r.substring(0,t+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}const V=new Array;function ye(r){V.push(r)}const B="NEEDLE_progressive",v=q("debugprogressive"),X=Symbol("needle-progressive-texture"),z=new Map,H=new Set;if(v){let r=function(){e+=1,console.log("Toggle LOD level",e,z),z.forEach((o,s)=>{for(const a of o.keys){const i=s[a];if(i.isBufferGeometry===!0){const l=O.getMeshLODInformation(i),d=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=e,O.assignMeshLOD(s,d),l&&(t=Math.max(t,l.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,O.assignTextureLOD(s,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,n=!1;window.addEventListener("keyup",o=>{o.key==="p"&&r(),o.key==="w"&&(n=!n,H&&H.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=n)}))})}function Z(r,e,t){var n;if(!v)return;z.has(r)||z.set(r,{keys:[],sourceId:t});const o=z.get(r);((n=o?.keys)==null?void 0:n.includes(e))==!1&&o.keys.push(e)}const L=class{constructor(r,e){u(this,"parser"),u(this,"url"),v&&console.log("Progressive extension registered for",e),this.parser=r,this.url=e}get name(){return B}static getMeshLODInformation(r){const e=this.getAssignedLODInformation(r);return e!=null&&e.key?this.lodInfos.get(e.key):null}static hasLODLevelAvailable(r,e){var t;if(r.isMaterial===!0){for(const s of Object.keys(r)){const a=r[s];if(a.isTexture&&this.hasLODLevelAvailable(a,e))return!0}return!1}else if(r.isGroup===!0){for(const s of r.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let n,o;if(r.isMesh?n=r.geometry:(r.isBufferGeometry||r.isTexture)&&(n=r),n&&(t=n?.userData)!=null&&t.LODS){const s=n.userData.LODS;if(o=this.lodInfos.get(s.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(r,e){var t;if(!r)return Promise.resolve(null);if(r instanceof W||r.isMesh===!0){const n=r.geometry,o=this.getAssignedLODInformation(n);if(!o)return Promise.resolve(null);for(const s of V)(t=s.onBeforeGetLODMesh)==null||t.call(s,r,e);return r["LOD:requested level"]=e,L.getOrLoadLOD(n,e).then(s=>{if(r["LOD:requested level"]===e){if(delete r["LOD:requested level"],Array.isArray(s)){const a=o.index||0;s=s[a]}s&&n!=s&&s instanceof U&&(r.geometry=s,v&&Z(r,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",r,s),null))}else v&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",r);return Promise.resolve(null)}static assignTextureLOD(r,e=0){if(!r)return Promise.resolve(null);if(r instanceof oe||r.isMaterial===!0){const t=r,n=[],o=new Array;if(v&&H.add(t),t instanceof ie)for(const s of Object.keys(t.uniforms)){const a=t.uniforms[s].value;if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,t,s);n.push(i),o.push(s)}}else for(const s of Object.keys(t)){const a=t[s];if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,t,s);n.push(i),o.push(s)}}return Promise.all(n).then(s=>{const a=new Array;for(let i=0;i<s.length;i++){const l=s[i],d=o[i];l&&l.isTexture===!0?a.push({material:t,slot:d,texture:l,level:e}):a.push({material:t,slot:d,texture:null,level:e})}return a})}if(r instanceof C||r.isTexture===!0){const t=r;return this.assignTextureLODForSlot(t,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(r,e,t,n){return r?.isTexture!==!0?Promise.resolve(null):L.getOrLoadLOD(r,e).then(o=>{if(Array.isArray(o))return null;if(o?.isTexture===!0){if(o!=r&&(t&&n&&(t[n]=o),v&&n&&t)){const s=this.getAssignedLODInformation(r);s&&Z(t,n,s.url)}return o}else v=="verbose"&&console.warn("No LOD found for",r,e);return null}).catch(o=>(console.error("Error loading LOD",r,o),null))}afterRoot(r){var e,t;return v&&console.log("AFTER",this.url,r),(e=this.parser.json.textures)==null||e.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[B];if(s){let a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===o&&(a=!0,L.registerTexture(this.url,i,o,s));a||this.parser.getDependency("texture",o).then(i=>{i&&L.registerTexture(this.url,i,o,s)})}}}),(t=this.parser.json.meshes)==null||t.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[B];if(s&&s.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===o&&L.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,n,o;const s=v=="verbose",a=r.userData.LODS;if(!a)return null;const i=a?.key;let l;if(r.isTexture===!0){const d=r;d.source&&d.source[X]&&(l=d.source[X])}if(l||(l=L.lodInfos.get(i)),l){if(e>0){let g=!1;const M=Array.isArray(l.lods);if(M&&e>=l.lods.length?g=!0:M||(g=!0),g)return this.lowresCache.get(i)}const d=Array.isArray(l.lods)?l.lods[e].path:l.lods;if(!d)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const c=me(a.url,d);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const g=c+"_"+l.guid,M=this.previouslyLoaded.get(g);if(M!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${g}`);let m=await M.catch(p=>(console.error(`Error loading LOD ${e} from ${c}
2
+ `,p),null)),f=!1;if(m==null||(m instanceof C&&r instanceof C?(t=m.image)!=null&&t.data||(n=m.source)!=null&&n.data?m=this.copySettings(r,m):(f=!0,this.previouslyLoaded.delete(g)):m instanceof U&&r instanceof U&&((o=m.attributes.position)!=null&&o.array||(f=!0,this.previouslyLoaded.delete(g)))),!f)return m}const y=l,P=new Promise(async(m,f)=>{const p=new ce;Q(p),v&&(await new Promise(D=>setTimeout(D,1e3)),s&&console.warn("Start loading (delayed) "+c,y.guid));let b=c;if(y&&Array.isArray(y.lods)){const D=y.lods[e];D.hash&&(b+="?v="+D.hash)}const x=await p.loadAsync(b).catch(D=>(console.error(`Error loading LOD ${e} from ${c}
3
+ `,D),null));if(!x)return null;const S=x.parser;s&&console.log("Loading finished "+c,y.guid);let T=0;if(x.parser.json.textures){let D=!1;for(const h of x.parser.json.textures){if(h!=null&&h.extensions){const w=h?.extensions[B];if(w!=null&&w.guid&&w.guid===y.guid){D=!0;break}}T++}if(D){let h=await S.getDependency("texture",T);return s&&console.log('change "'+r.name+'" \u2192 "'+h.name+'"',c,T,h,g),r instanceof C&&(h=this.copySettings(r,h)),h&&(h.guid=y.guid),m(h)}}if(T=0,x.parser.json.meshes){let D=!1;for(const h of x.parser.json.meshes){if(h!=null&&h.extensions){const w=h?.extensions[B];if(w!=null&&w.guid&&w.guid===y.guid){D=!0;break}}T++}if(D){const h=await S.getDependency("mesh",T),w=y;if(s&&console.log(`Loaded Mesh "${h.name}"`,c,T,h,g),h.isMesh===!0){const _=h.geometry;return L.assignLODInformation(a.url,_,i,e,void 0,w.density),m(_)}else{const _=new Array;for(let I=0;I<h.children.length;I++){const F=h.children[I];if(F instanceof W){const j=F.geometry;L.assignLODInformation(a.url,j,i,e,I,w.density),_.push(j)}}return m(_)}}}return m(null)});return this.previouslyLoaded.set(g,P),await P}else if(r instanceof C){s&&console.log("Load texture from uri: "+c);const g=await new ae().loadAsync(c);return g?(g.guid=l.guid,g.flipY=!1,g.needsUpdate=!0,g.colorSpace=r.colorSpace,s&&console.log(l,g)):v&&console.warn("failed loading",c),g}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,n,o,s){if(!e)return;e.userData||(e.userData={});const a=new xe(r,t,n,o,s);e.userData.LODS=a,e.userData.LOD=n}static getAssignedLODInformation(r){var e;return((e=r?.userData)==null?void 0:e.LODS)||null}static copySettings(r,e){return this._copiedTextures.get(r)||(e=e.clone(),this._copiedTextures.set(r,e),e.offset=r.offset,e.repeat=r.repeat,e.colorSpace=r.colorSpace,e)}};let O=L;u(O,"registerTexture",(r,e,t,n)=>{v&&console.log("> Progressive: register texture",t,e.name,e.uuid,e,n),e.source&&(e.source[X]=n);const o=n.guid;L.assignLODInformation(r,e,o,0,0,void 0),L.lodInfos.set(o,n),L.lowresCache.set(o,e)}),u(O,"registerMesh",(r,e,t,n,o,s)=>{var a;v&&console.log("> Progressive: register mesh",o,t.name,s,t.uuid,t);const i=t.geometry;i.userData||(i.userData={}),L.assignLODInformation(r,i,e,n,o,s.density),L.lodInfos.set(e,s);let l=L.lowresCache.get(e);l?l.push(t.geometry):l=[t.geometry],L.lowresCache.set(e,l);for(const d of V)(a=d.onRegisteredNewMesh)==null||a.call(d,t,s)}),u(O,"lodInfos",new Map),u(O,"previouslyLoaded",new Map),u(O,"lowresCache",new Map),u(O,"_copiedTextures",new Map);class xe{constructor(e,t,n,o,s){u(this,"url"),u(this,"key"),u(this,"level"),u(this,"index"),u(this,"density"),this.url=e,this.key=t,this.level=n,o!=null&&(this.index=o),s!=null&&(this.density=s)}}const ee=q("debugprogressive"),De=q("noprogressive"),A=class{constructor(r){u(this,"renderer"),u(this,"projectionScreenMatrix",new K),u(this,"cameraFrustrum",new le),u(this,"updateInterval",0),u(this,"pause",!1),u(this,"plugins",[]),u(this,"_originalRender"),u(this,"_sphere",new ue),u(this,"_tempBox",new Y),u(this,"tempMatrix",new K),u(this,"_tempWorldPosition",new E),u(this,"_tempBoxSize",new E),u(this,"_tempBox2Size",new E),this.renderer=r}static getObjectLODState(r){var e;return(e=r.userData)==null?void 0:e.LOD_state}enable(){if(this._originalRender)return;let r=0;this._originalRender=this.renderer.render;const e=this;let t=0;J(this.renderer),this.renderer.render=function(n,o){const s=t++,a=r++;e.onBeforeRender(n,o,a,s),e._originalRender.call(this,n,o),e.onAfterRender(n,o,a,s),r--}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(r,e,t,n){}onAfterRender(r,e,t,n){if(De||this.pause||this.updateInterval>0&&n%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const o=1e5,s=this.renderer.renderLists.get(r,t).opaque;for(const a of s){const i=a.object;(i instanceof W||i.isMesh)&&this.updateLODs(r,e,i,o)}}updateLODs(r,e,t,n){var o,s;for(const d of this.plugins)(o=d.onBeforeUpdateLOD)==null||o.call(d,this.renderer,r,e,t);let a=t.userData.LOD_state;a||(a=new ve,t.userData.LOD_state=a);let i=this.calculateLodLevel(e,t,a,n);i=Math.round(i),i>=0&&this.loadProgressiveMeshes(t,i);let l=0;if(t.material){const d=t["DEBUG:LOD"];if(d!=null&&(l=d),Array.isArray(t.material))for(const c of t.material)this.loadProgressiveTextures(c,l);else this.loadProgressiveTextures(t.material,l)}for(const d of this.plugins)(s=d.onAfterUpdatedLOD)==null||s.call(d,this.renderer,r,e,t,i);a.lastLodLevel=i}loadProgressiveTextures(r,e){return r&&r.userData&&r.userData.LOD!==e?(r.userData.LOD=e,O.assignTextureLOD(r,e)):Promise.resolve(null)}loadProgressiveMeshes(r,e){if(!r)return Promise.resolve(null);if(r.userData||(r.userData={}),r.userData.LOD!==e){r.userData.LOD=e;const t=r.geometry;return O.assignMeshLOD(r,e).then(n=>(n&&r.userData.LOD==e&&t!=r.geometry,n))}return Promise.resolve(null)}calculateLodLevel(r,e,t,n){var o;if(!e)return-1;let s=10+1;if(r){if(ee&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=O.getMeshLODInformation(e.geometry),i=a?.lods;if(!i||i.length<=0||!((o=this.cameraFrustrum)!=null&&o.intersectsObject(e)))return 99;const l=e.geometry.boundingBox;if(l&&r.isPerspectiveCamera){const d=r;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 f=r.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f))return 0}if(this._tempBox.copy(l),this._tempBox.applyMatrix4(e.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&d.fov>70){const f=this._tempBox.min,p=this._tempBox.max;let b=f.x,x=f.y,S=p.x,T=p.y;const D=2,h=1.5,w=(f.x+p.x)*.5,_=(f.y+p.y)*.5;b=(b-w)*D+w,x=(x-_)*D+_,S=(S-w)*D+w,T=(T-_)*D+_;const I=b<0&&S>0?0:Math.min(Math.abs(f.x),Math.abs(p.x)),F=x<0&&T>0?0:Math.min(Math.abs(f.y),Math.abs(p.y)),j=Math.max(I,F);t.lastCentrality=(h-j)*(h-j)*(h-j)}else t.lastCentrality=1;const c=this._tempBox.getSize(this._tempBoxSize);c.multiplyScalar(.5),screen.availHeight>0&&c.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),c.x*=d.aspect;const g=r.matrixWorldInverse,M=new Y;M.copy(l),M.applyMatrix4(e.matrixWorld),M.applyMatrix4(g);const y=M.getSize(this._tempBox2Size),P=Math.max(y.x,y.y);if(Math.max(c.x,c.y)!=0&&P!=0&&(c.z=y.z/Math.max(y.x,y.y)*Math.max(c.x,c.y)),t.lastScreenCoverage=Math.max(c.x,c.y,c.z),t.lastScreenspaceVolume.copy(c),t.lastScreenCoverage*=t.lastCentrality,ee&&A.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const p=A.corner0,b=A.corner1,x=A.corner2,S=A.corner3;p.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=p.x,x.copy(this._tempBox.max),x.y=p.y,S.copy(this._tempBox.max);const T=(p.z+S.z)*.5;p.z=b.z=x.z=S.z=T,p.applyMatrix4(f),b.applyMatrix4(f),x.applyMatrix4(f),S.applyMatrix4(f),A.debugDrawLine(p,b,255),A.debugDrawLine(p,x,255),A.debugDrawLine(b,S,255),A.debugDrawLine(x,S,255)}let m=999;if(i&&t.lastScreenCoverage>0){for(let f=0;f<i.length;f++)if(i[f].density/t.lastScreenCoverage<n){m=f;break}}m<s&&(s=m)}}return s}};let k=A;u(k,"debugDrawLine"),u(k,"corner0",new E),u(k,"corner1",new E),u(k,"corner2",new E),u(k,"corner3",new E);class ve{constructor(){u(this,"lastLodLevel",0),u(this,"lastScreenCoverage",0),u(this,"lastScreenspaceVolume",new E),u(this,"lastCentrality",0)}}const re=Symbol("NEEDLE_mesh_lod"),N=Symbol("NEEDLE_texture_lod");function te(r){if(!r)return null;let e=null,t=null;for(let n=r;n!=null;n=Object.getPrototypeOf(n)){const o=Object.getOwnPropertySymbols(n),s=o.find(i=>i.toString()=="Symbol(renderer)"),a=o.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=r[s].threeRenderer),!t&&a!=null&&(t=r[a])}if(e){console.log("Adding Needle LODs to modelviewer");const n=new k(e);if(n.plugins.push(new Le(r)),n.enable(),t){const o=t.camera||t.traverse(s=>s.type=="PerspectiveCamera")[0];o&&e.render(t,o)}return()=>{n.disable()}}return null}class Le{constructor(e){u(this,"modelviewer"),this.modelviewer=e}onBeforeUpdateLOD(e,t,n,o){this.tryParseMeshLOD(t,o),this.tryParseTextureLOD(t,o)}getUrl(){return this.modelviewer.getAttribute("src")}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[N]==!0)return;t[N]=!0;const n=this.tryGetCurrentGLTF(e),o=this.getUrl();if(!o){console.error("No url found in modelviewer");return}if(n&&t.material){let s=function(i){var l,d,c;if(i[N]==!0)return;i[N]=!0,i.userData&&(i.userData.LOD=-1);const g=Object.keys(i);for(let M=0;M<g.length;M++){const y=g[M],P=i[y];if(P?.isTexture===!0){const m=(d=(l=P.userData)==null?void 0:l.associations)==null?void 0:d.textures,f=n.parser.json.textures[m];if((c=f.extensions)!=null&&c[B]){const p=f.extensions[B];p&&o&&O.registerTexture(o,P,p.lods.length,p)}}}};const a=t.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,t){var n,o;if(t[re]==!0)return;t[re]=!0;const s=this.getUrl();if(!s){console.error("No url found in modelviewer");return}const a=(o=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[B];if(a&&s){const i=t.uuid;O.registerMesh(s,i,t,0,a.lods.length,a)}}}function Oe(r,e,t,n){J(e),Q(t),t.register(s=>new O(s,r));const o=new k(e);return n?.enableLODsManager!==!1&&o.enable(),o}document.addEventListener("DOMContentLoaded",()=>{te(document.querySelector("model-viewer"))});export{B as EXTENSION_NAME,k as LODsManager,O as NEEDLE_progressive,te as patchModelViewer,ye as registerPlugin,Oe as useNeedleProgressive};
@@ -0,0 +1,3 @@
1
+ "use strict";var re=Object.defineProperty;var se=(l,e,t)=>e in l?re(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var u=(l,e,t)=>(se(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("three"),ie=require("three/examples/jsm/loaders/GLTFLoader.js"),oe=require("three/examples/jsm/libs/meshopt_decoder.module.js"),ne=require("three/examples/jsm/loaders/DRACOLoader.js"),ae=require("three/examples/jsm/loaders/KTX2Loader.js"),le="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ue="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";let z,$,U;function Z(l){z||(z=new ne.DRACOLoader,z.setDecoderPath(le),z.setDecoderConfig({type:"js"})),U||(U=new ae.KTX2Loader,U.setTranscoderPath(ue)),$||($=oe.MeshoptDecoder),l?U.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function j(l){l.dracoLoader||l.setDRACOLoader(z),l.ktx2Loader||l.setKTX2Loader(U),l.meshoptDecoder||l.setMeshoptDecoder($)}function Y(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function ce(l,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||l===void 0)return e;const t=l.lastIndexOf("/");if(t>=0){const r=l.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}const X=new Array;function fe(l){X.push(l)}const k="NEEDLE_progressive",T=Y("debugprogressive"),V=Symbol("needle-progressive-texture"),I=new Map,K=new Set;if(T){let l=function(){e+=1,console.log("Toggle LOD level",e,I),I.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n.isBufferGeometry===!0){const a=w.getMeshLODInformation(n),g=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,w.assignMeshLOD(o,g),a&&(t=Math.max(t,a.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,w.assignTextureLOD(o,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&l(),i.key==="w"&&(r=!r,K&&K.forEach(o=>{o.name!="BackgroundCubeMaterial"&&"wireframe"in o&&(o.wireframe=r)}))})}function H(l,e,t){var i;if(!T)return;I.has(l)||I.set(l,{keys:[],sourceId:t});const r=I.get(l);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const S=class{constructor(e,t){u(this,"parser");u(this,"url");T&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return k}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static hasLODLevelAvailable(e,t){var o;if(e.isMaterial===!0){for(const s of Object.keys(e)){const n=e[s];if(n.isTexture&&this.hasLODLevelAvailable(n,t))return!0}return!1}else if(e.isGroup===!0){for(const s of e.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,t))return!0}let r,i;if(e.isMesh?r=e.geometry:(e.isBufferGeometry||e.isTexture)&&(r=e),r&&(o=r==null?void 0:r.userData)!=null&&o.LODS){const s=r.userData.LODS;if(i=this.lodInfos.get(s.key),t===void 0)return i!=null;if(i)return Array.isArray(i.lods)?t<i.lods.length:t===0}return!1}static assignMeshLOD(e,t){var r;if(!e)return Promise.resolve(null);if(e instanceof h.Mesh||e.isMesh===!0){const i=e.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of X)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,S.getOrLoadLOD(i,t).then(s=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(s)){const n=o.index||0;s=s[n]}s&&i!=s&&s instanceof h.BufferGeometry&&(e.geometry=s,T&&H(e,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else T&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",e);return Promise.resolve(null)}static assignTextureLOD(e,t=0){if(!e)return Promise.resolve(null);if(e instanceof h.Material||e.isMaterial===!0){const r=e,i=[],o=new Array;if(T&&K.add(r),r instanceof h.RawShaderMaterial)for(const s of Object.keys(r.uniforms)){const n=r.uniforms[s].value;if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,s);i.push(a),o.push(s)}}else for(const s of Object.keys(r)){const n=r[s];if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,s);i.push(a),o.push(s)}}return Promise.all(i).then(s=>{const n=new Array;for(let a=0;a<s.length;a++){const g=s[a],f=o[a];g&&g.isTexture===!0?n.push({material:r,slot:f,texture:g,level:t}):n.push({material:r,slot:f,texture:null,level:t})}return n})}if(e instanceof h.Texture||e.isTexture===!0){const r=e;return this.assignTextureLODForSlot(r,t,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(e,t,r,i){return(e==null?void 0:e.isTexture)!==!0?Promise.resolve(null):S.getOrLoadLOD(e,t).then(o=>{if(Array.isArray(o))return null;if((o==null?void 0:o.isTexture)===!0){if(o!=e&&(r&&i&&(r[i]=o),T&&i&&r)){const s=this.getAssignedLODInformation(e);s&&H(r,i,s.url)}return o}else T=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(o=>(console.error("Error loading LOD",e,o),null))}afterRoot(e){var t,r;return T&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s){let n=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===o&&(n=!0,S.registerTexture(this.url,a,o,s));n||this.parser.getDependency("texture",o).then(a=>{a&&S.registerTexture(this.url,a,o,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s&&s.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const a=this.parser.associations.get(n);a.meshes===o&&S.registerMesh(this.url,s.guid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,g;const r=T=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const f=e;f.source&&f.source[V]&&(s=f.source[V])}if(s||(s=S.lodInfos.get(o)),s){if(t>0){let d=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?d=!0:M||(d=!0),d)return this.lowresCache.get(o)}const f=Array.isArray(s.lods)?s.lods[t].path:s.lods;if(!f)return T&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const D=ce(i.url,f);if(D.endsWith(".glb")||D.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const d=D+"_"+s.guid,M=this.previouslyLoaded.get(d);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${d}`);let m=await M.catch(F=>(console.error(`Error loading LOD ${t} from ${D}
2
+ `,F),null)),R=!1;if(m==null||(m instanceof h.Texture&&e instanceof h.Texture?(n=m.image)!=null&&n.data||(a=m.source)!=null&&a.data?m=this.copySettings(e,m):(R=!0,this.previouslyLoaded.delete(d)):m instanceof h.BufferGeometry&&e instanceof h.BufferGeometry&&((g=m.attributes.position)!=null&&g.array||(R=!0,this.previouslyLoaded.delete(d)))),!R)return m}const x=s,b=new Promise(async(m,R)=>{const F=new ie.GLTFLoader;j(F),T&&(await new Promise(y=>setTimeout(y,1e3)),r&&console.warn("Start loading (delayed) "+D,x.guid));let L=D;if(x&&Array.isArray(x.lods)){const y=x.lods[t];y.hash&&(L+="?v="+y.hash)}const p=await F.loadAsync(L).catch(y=>(console.error(`Error loading LOD ${t} from ${D}
3
+ `,y),null));if(!p)return null;const P=p.parser;r&&console.log("Loading finished "+D,x.guid);let O=0;if(p.parser.json.textures){let y=!1;for(const c of p.parser.json.textures){if(c!=null&&c.extensions){const v=c==null?void 0:c.extensions[k];if(v!=null&&v.guid&&v.guid===x.guid){y=!0;break}}O++}if(y){let c=await P.getDependency("texture",O);return r&&console.log('change "'+e.name+'" → "'+c.name+'"',D,O,c,d),e instanceof h.Texture&&(c=this.copySettings(e,c)),c&&(c.guid=x.guid),m(c)}}if(O=0,p.parser.json.meshes){let y=!1;for(const c of p.parser.json.meshes){if(c!=null&&c.extensions){const v=c==null?void 0:c.extensions[k];if(v!=null&&v.guid&&v.guid===x.guid){y=!0;break}}O++}if(y){const c=await P.getDependency("mesh",O),v=x;if(r&&console.log(`Loaded Mesh "${c.name}"`,D,O,c,d),c.isMesh===!0){const B=c.geometry;return S.assignLODInformation(i.url,B,o,t,void 0,v.density),m(B)}else{const B=new Array;for(let C=0;C<c.children.length;C++){const E=c.children[C];if(E instanceof h.Mesh){const N=E.geometry;S.assignLODInformation(i.url,N,o,t,C,v.density),B.push(N)}}return m(B)}}}return m(null)});return this.previouslyLoaded.set(d,b),await b}else if(e instanceof h.Texture){r&&console.log("Load texture from uri: "+D);const M=await new h.TextureLoader().loadAsync(D);return M?(M.guid=s.guid,M.flipY=!1,M.needsUpdate=!0,M.colorSpace=e.colorSpace,r&&console.log(s,M)):T&&console.warn("failed loading",D),M}}else T&&console.warn(`Can not load LOD ${t}: no LOD info found for "${o}" ${e.name}`,e.type);return null}static assignLODInformation(e,t,r,i,o,s){if(!t)return;t.userData||(t.userData={});const n=new de(e,r,i,o,s);t.userData.LODS=n,t.userData.LOD=i}static getAssignedLODInformation(e){var t;return((t=e==null?void 0:e.userData)==null?void 0:t.LODS)||null}static copySettings(e,t){const r=this._copiedTextures.get(e);return r||(t=t.clone(),this._copiedTextures.set(e,t),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t)}};let w=S;u(w,"registerTexture",(e,t,r,i)=>{T&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[V]=i);const o=i.guid;S.assignLODInformation(e,t,o,0,0,void 0),S.lodInfos.set(o,i),S.lowresCache.set(o,t)}),u(w,"registerMesh",(e,t,r,i,o,s)=>{var g;T&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;n.userData||(n.userData={}),S.assignLODInformation(e,n,t,i,o,s.density),S.lodInfos.set(t,s);let a=S.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],S.lowresCache.set(t,a);for(const f of X)(g=f.onRegisteredNewMesh)==null||g.call(f,r,s)}),u(w,"lodInfos",new Map),u(w,"previouslyLoaded",new Map),u(w,"lowresCache",new Map),u(w,"_copiedTextures",new Map);class de{constructor(e,t,r,i,o){u(this,"url");u(this,"key");u(this,"level");u(this,"index");u(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const J=Y("debugprogressive"),ge=Y("noprogressive"),A=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new h.Matrix4);u(this,"cameraFrustrum",new h.Frustum);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"plugins",[]);u(this,"_originalRender");u(this,"_sphere",new h.Sphere);u(this,"_tempBox",new h.Box3);u(this,"tempMatrix",new h.Matrix4);u(this,"_tempWorldPosition",new h.Vector3);u(this,"_tempBoxSize",new h.Vector3);u(this,"_tempBox2Size",new h.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;let r=0;Z(this.renderer),this.renderer.render=function(i,o){const s=r++,n=e++;t.onBeforeRender(i,o,n,s),t._originalRender.call(this,i,o),t.onAfterRender(i,o,n,s),e--}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){if(ge||this.pause||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const o=1e5,n=this.renderer.renderLists.get(e,r).opaque;for(const a of n){const g=a.object;(g instanceof h.Mesh||g.isMesh)&&this.updateLODs(e,t,g,o)}}updateLODs(e,t,r,i){var a,g;for(const f of this.plugins)(a=f.onBeforeUpdateLOD)==null||a.call(f,this.renderer,e,t,r);let o=r.userData.LOD_state;o||(o=new he,r.userData.LOD_state=o);let s=this.calculateLodLevel(t,r,o,i);s=Math.round(s),s>=0&&this.loadProgressiveMeshes(r,s);let n=0;if(r.material){const f=r["DEBUG:LOD"];if(f!=null&&(n=f),Array.isArray(r.material))for(const D of r.material)this.loadProgressiveTextures(D,n);else this.loadProgressiveTextures(r.material,n)}for(const f of this.plugins)(g=f.onAfterUpdatedLOD)==null||g.call(f,this.renderer,e,t,r,s);o.lastLodLevel=s}loadProgressiveTextures(e,t){return e&&e.userData&&e.userData.LOD!==t?(e.userData.LOD=t,w.assignTextureLOD(e,t)):Promise.resolve(null)}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return w.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}calculateLodLevel(e,t,r,i){var n;if(!t)return-1;let s=10+1;if(e){if(J&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const a=w.getMeshLODInformation(t.geometry),g=a==null?void 0:a.lods;if(!g||g.length<=0||!((n=this.cameraFrustrum)!=null&&n.intersectsObject(t)))return 99;const f=t.geometry.boundingBox;if(f&&e.isPerspectiveCamera){const D=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const L=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(L))return 0}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(t.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&D.fov>70){const L=this._tempBox.min,p=this._tempBox.max;let P=L.x,O=L.y,y=p.x,c=p.y;const v=2,B=1.5,C=(L.x+p.x)*.5,E=(L.y+p.y)*.5;P=(P-C)*v+C,O=(O-E)*v+E,y=(y-C)*v+C,c=(c-E)*v+E;const N=P<0&&y>0?0:Math.min(Math.abs(L.x),Math.abs(p.x)),te=O<0&&c>0?0:Math.min(Math.abs(L.y),Math.abs(p.y)),q=Math.max(N,te);r.lastCentrality=(B-q)*(B-q)*(B-q)}else r.lastCentrality=1;const d=this._tempBox.getSize(this._tempBoxSize);d.multiplyScalar(.5),screen.availHeight>0&&d.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),d.x*=D.aspect;const M=e.matrixWorldInverse,x=new h.Box3;x.copy(f),x.applyMatrix4(t.matrixWorld),x.applyMatrix4(M);const b=x.getSize(this._tempBox2Size),G=Math.max(b.x,b.y);if(Math.max(d.x,d.y)!=0&&G!=0&&(d.z=b.z/Math.max(b.x,b.y)*Math.max(d.x,d.y)),r.lastScreenCoverage=Math.max(d.x,d.y,d.z),r.lastScreenspaceVolume.copy(d),r.lastScreenCoverage*=r.lastCentrality,J&&A.debugDrawLine){const L=this.tempMatrix.copy(this.projectionScreenMatrix);L.invert();const p=A.corner0,P=A.corner1,O=A.corner2,y=A.corner3;p.copy(this._tempBox.min),P.copy(this._tempBox.max),P.x=p.x,O.copy(this._tempBox.max),O.y=p.y,y.copy(this._tempBox.max);const c=(p.z+y.z)*.5;p.z=P.z=O.z=y.z=c,p.applyMatrix4(L),P.applyMatrix4(L),O.applyMatrix4(L),y.applyMatrix4(L),A.debugDrawLine(p,P,255),A.debugDrawLine(p,O,255),A.debugDrawLine(P,y,255),A.debugDrawLine(O,y,255)}let R=999;if(g&&r.lastScreenCoverage>0){for(let L=0;L<g.length;L++)if(g[L].density/r.lastScreenCoverage<i){R=L;break}}R<s&&(s=R)}}return s}};let _=A;u(_,"debugDrawLine"),u(_,"corner0",new h.Vector3),u(_,"corner1",new h.Vector3),u(_,"corner2",new h.Vector3),u(_,"corner3",new h.Vector3);class he{constructor(){u(this,"lastLodLevel",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new h.Vector3);u(this,"lastCentrality",0)}}const Q=Symbol("NEEDLE_mesh_lod"),W=Symbol("NEEDLE_texture_lod");function ee(l){if(!l)return null;let e=null,t=null;for(let r=l;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),o=i.find(n=>n.toString()=="Symbol(renderer)"),s=i.find(n=>n.toString()=="Symbol(scene)");!e&&o!=null&&(e=l[o].threeRenderer),!t&&s!=null&&(t=l[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=new _(e);if(r.plugins.push(new pe(l)),r.enable(),t){const i=t.camera||t.traverse(o=>o.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class pe{constructor(e){u(this,"modelviewer");this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){return this.modelviewer.getAttribute("src")}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[W]==!0)return;t[W]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(!i){console.error("No url found in modelviewer");return}if(r&&t.material){let o=function(n){var g,f,D;if(n[W]==!0)return;n[W]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let d=0;d<a.length;d++){const M=a[d],x=n[M];if((x==null?void 0:x.isTexture)===!0){const b=(f=(g=x.userData)==null?void 0:g.associations)==null?void 0:f.textures,G=r.parser.json.textures[b];if((D=G.extensions)!=null&&D[k]){const m=G.extensions[k];m&&i&&w.registerTexture(i,x,m.lods.length,m)}}}};const s=t.material;if(Array.isArray(s))for(const n of s)o(n);else o(s)}}tryParseMeshLOD(e,t){var o,s;if(t[Q]==!0)return;t[Q]=!0;const r=this.getUrl();if(!r){console.error("No url found in modelviewer");return}const i=(s=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:s[k];if(i&&r){const n=t.uuid;w.registerMesh(r,n,t,0,i.lods.length,i)}}}function ye(l,e,t,r){Z(e),j(t),t.register(o=>new w(o,l));const i=new _(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ee(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=k;exports.LODsManager=_;exports.NEEDLE_progressive=w;exports.patchModelViewer=ee;exports.registerPlugin=fe;exports.useNeedleProgressive=ye;