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

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
@@ -5,5 +5,5 @@ 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
7
 
8
- ## [1.0.0-alpha.2] - 2023-04-29
8
+ ## [1.0.0-alpha.5] - 2023-04-30
9
9
  - 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) {
@@ -257,20 +258,20 @@ const v = class {
257
258
  const g = L + "_" + s.guid, w = this.previouslyLoaded.get(g);
258
259
  if (w !== void 0) {
259
260
  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;
261
+ let m = await w.catch((G) => (console.error(`Error loading LOD ${t} from ${L}
262
+ `, G), null)), B = !1;
262
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(g)) : m instanceof X && e instanceof X && ((d = m.attributes.position) != null && d.array || (B = !0, this.previouslyLoaded.delete(g)))), !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;
@@ -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(g, 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);
@@ -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;
@@ -435,10 +449,6 @@ const we = Q("noprogressive"), A = class {
435
449
  (d instanceof Y || d.isMesh) && this.updateLODs(e, t, d, 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
454
  var a, d;
@@ -518,14 +528,14 @@ const we = Q("noprogressive"), A = class {
518
528
  g.multiplyScalar(0.5), screen.availHeight > 0 && g.multiplyScalar(this.renderer.domElement.clientHeight / screen.availHeight), g.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(g.x, g.y) != 0 && E != 0 && (g.z = A.z / Math.max(A.x, A.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 && 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
541
  if (d && r.lastScreenCoverage > 0) {
@@ -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) {
@@ -600,9 +618,9 @@ class Me {
600
618
  for (let g = 0; g < a.length; g++) {
601
619
  const w = a[g], 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 = (d = D.userData) == null ? void 0 : d.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 i of o.keys){const a=s[i];if(a.isBufferGeometry===!0){const l=O.getMeshLODInformation(a),d=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=d,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 i=r[s];if(i.isTexture&&this.hasLODLevelAvailable(i,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 i=o.index||0;s=s[i]}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 i=t.uniforms[s].value;if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);n.push(a),o.push(s)}}else for(const s of Object.keys(t)){const i=t[s];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);n.push(a),o.push(s)}}return Promise.all(n).then(s=>{const i=new Array;for(let a=0;a<s.length;a++){const l=s[a],d=o[a];l&&l.isTexture===!0?i.push({material:t,slot:d,texture:l,level:e}):i.push({material:t,slot:d,texture:null,level:e})}return i})}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 i=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===o&&(i=!0,L.registerTexture(this.url,a,o,s));i||this.parser.getDependency("texture",o).then(a=>{a&&L.registerTexture(this.url,a,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 i of this.parser.associations.keys())if(i.isMesh){const a=this.parser.associations.get(i);a.meshes===o&&L.registerMesh(this.url,i.uuid,i,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,n,o;const s=v=="verbose",i=r.userData.LODS;if(!i)return null;const a=i?.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(a)),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(a)}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(i.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(i.url,_,a,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(i.url,j,a,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 "${a}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,n,o,s){if(!e)return;e.userData||(e.userData={});const i=new xe(r,t,n,o,s);e.userData.LODS=i,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=e.uuid;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 i;v&&console.log("> Progressive: register mesh",o,t.name,s,t.uuid,t);const a=t.geometry;a.userData||(a.userData={}),L.assignLODInformation(r,a,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)(i=d.onRegisteredNewMesh)==null||i.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++,i=r++;e.onBeforeRender(n,o,i,s),e._originalRender.call(this,n,o),e.onAfterRender(n,o,i,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 i of s){const a=i.object;(a instanceof W||a.isMesh)&&this.updateLODs(r,e,a,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 i=t.userData.LOD_state;i||(i=new ve,t.userData.LOD_state=i);let a=this.calculateLodLevel(e,t,i,n);a=Math.round(a),a>=0&&this.loadProgressiveMeshes(t,a);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,a);i.lastLodLevel=a}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 i=O.getMeshLODInformation(e.geometry),a=i?.lods;if(!a||a.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(a&&t.lastScreenCoverage>0){for(let f=0;f<a.length;f++)if(a[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(a=>a.toString()=="Symbol(renderer)"),i=o.find(a=>a.toString()=="Symbol(scene)");!e&&s!=null&&(e=r[s].threeRenderer),!t&&i!=null&&(t=r[i])}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(a){var l,d,c;if(a[N]==!0)return;a[N]=!0,a.userData&&(a.userData.LOD=-1);const g=Object.keys(a);for(let M=0;M<g.length;M++){const y=g[M],P=a[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 i=t.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}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 i=(o=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[B];if(i&&s){const a=t.uuid;O.registerMesh(s,a,t,0,i.lods.length,i)}}}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),d=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=d,w.assignMeshLOD(o,d),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 d=s[a],f=o[a];d&&d.isTexture===!0?n.push({material:r,slot:f,texture:d,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,n.uuid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,d;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 g=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?g=!0:M||(g=!0),g)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 g=D+"_"+s.guid,M=this.previouslyLoaded.get(g);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${g}`);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(g)):m instanceof h.BufferGeometry&&e instanceof h.BufferGeometry&&((d=m.attributes.position)!=null&&d.array||(R=!0,this.previouslyLoaded.delete(g)))),!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,g),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,g),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(g,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=t.uuid;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 d;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)(d=f.onRegisteredNewMesh)==null||d.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 d=a.object;(d instanceof h.Mesh||d.isMesh)&&this.updateLODs(e,t,d,o)}}updateLODs(e,t,r,i){var a,d;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)(d=f.onAfterUpdatedLOD)==null||d.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),d=a==null?void 0:a.lods;if(!d||d.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 g=this._tempBox.getSize(this._tempBoxSize);g.multiplyScalar(.5),screen.availHeight>0&&g.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),g.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(g.x,g.y)!=0&&G!=0&&(g.z=b.z/Math.max(b.x,b.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,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(d&&r.lastScreenCoverage>0){for(let L=0;L<d.length;L++)if(d[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 d,f,D;if(n[W]==!0)return;n[W]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let g=0;g<a.length;g++){const M=a[g],x=n[M];if((x==null?void 0:x.isTexture)===!0){const b=(f=(d=x.userData)==null?void 0:d.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;
@@ -1,109 +1,109 @@
1
- import { BufferGeometry, Material, Mesh, Texture } from "three";
2
- import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
3
- export declare const EXTENSION_NAME = "NEEDLE_progressive";
4
- declare type NEEDLE_progressive_model_LOD = {
5
- path: string;
6
- hash?: string;
7
- };
8
- /** This is the data structure we have in the NEEDLE_progressive extension */
9
- declare type NEEDLE_progressive_model = {
10
- guid: string;
11
- lods: Array<NEEDLE_progressive_model_LOD>;
12
- };
13
- export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {};
14
- export declare type NEEDLE_progressive_mesh_model = NEEDLE_progressive_model & {
15
- density: number;
16
- lods: Array<NEEDLE_progressive_model_LOD & {
17
- density: number;
18
- indexCount: number;
19
- vertexCount: number;
20
- }>;
21
- };
22
- /**
23
- * This is the result of a progressive texture loading event for a material's texture slot in {@link NEEDLE_progressive.assignTextureLOD}
24
- * @internal
25
- */
26
- export declare type ProgressiveMaterialTextureLoadingResult = {
27
- /** the material the progressive texture was loaded for */
28
- material: Material;
29
- /** the slot in the material where the texture was loaded */
30
- slot: string;
31
- /** the texture that was loaded (if any) */
32
- texture: Texture | null;
33
- /** the level of detail that was loaded */
34
- level: number;
35
- };
36
- /**
37
- * The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
38
- * This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
39
- * @example
40
- * ```javascript
41
- * const loader = new GLTFLoader();
42
- * loader.register(new NEEDLE_progressive());
43
- * loader.load("model.glb", (gltf) => {
44
- * const mesh = gltf.scene.children[0] as Mesh;
45
- * NEEDLE_progressive.assignMeshLOD(context, sourceId, mesh, 1).then(mesh => {
46
- * console.log("Mesh with LOD level 1 loaded", mesh);
47
- * });
48
- * });
49
- * ```
50
- */
51
- export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
52
- /** The name of the extension */
53
- get name(): string;
54
- static getMeshLODInformation(geo: BufferGeometry): NEEDLE_progressive_mesh_model | null;
55
- /** Check if a LOD level is available for a mesh or a texture
56
- * @param obj the mesh or texture to check
57
- * @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
58
- * @returns true if the LOD level is available (or if any LOD level is available if level is undefined)
59
- */
60
- static hasLODLevelAvailable(obj: Mesh | BufferGeometry | Texture | Material, level?: number): boolean;
61
- /** Load a different resolution of a mesh (if available)
62
- * @param context the context
63
- * @param source the sourceid of the file from which the mesh is loaded (this is usually the component's sourceId)
64
- * @param mesh the mesh to load the LOD for
65
- * @param level the level of detail to load (0 is the highest resolution)
66
- * @returns a promise that resolves to the mesh with the requested LOD level
67
- * @example
68
- * ```javascript
69
- * const mesh = this.gameObject as Mesh;
70
- * NEEDLE_progressive.assignMeshLOD(context, sourceId, mesh, 1).then(mesh => {
71
- * console.log("Mesh with LOD level 1 loaded", mesh);
72
- * });
73
- * ```
74
- */
75
- static assignMeshLOD(mesh: Mesh, level: number): Promise<BufferGeometry | null>;
76
- /** Load a different resolution of a texture (if available)
77
- * @param context the context
78
- * @param source the sourceid of the file from which the texture is loaded (this is usually the component's sourceId)
79
- * @param materialOrTexture the material or texture to load the LOD for (if passing in a material all textures in the material will be loaded)
80
- * @param level the level of detail to load (0 is the highest resolution) - currently only 0 is supported
81
- * @returns a promise that resolves to the material or texture with the requested LOD level
82
- */
83
- static assignTextureLOD(materialOrTexture: Material | Texture, level?: number): Promise<Array<ProgressiveMaterialTextureLoadingResult> | Texture | null>;
84
- private static assignTextureLODForSlot;
85
- private readonly parser;
86
- private readonly url;
87
- constructor(parser: GLTFParser, url: string);
88
- afterRoot(gltf: GLTF): null;
89
- /**
90
- * Register a texture with LOD information
91
- */
92
- static registerTexture: (url: string, tex: Texture, index: number, ext: NEEDLE_progressive_texture_model) => void;
93
- /**
94
- * Register a mesh with LOD information
95
- */
96
- static registerMesh: (url: string, key: string, mesh: Mesh, level: number, index: number | undefined, ext: NEEDLE_progressive_mesh_model) => void;
97
- /** A map of key = asset uuid and value = LOD information */
98
- private static readonly lodInfos;
99
- /** cache of already loaded mesh lods */
100
- private static readonly previouslyLoaded;
101
- /** this contains the geometry/textures that were originally loaded */
102
- private static readonly lowresCache;
103
- private static getOrLoadLOD;
104
- private static assignLODInformation;
105
- private static getAssignedLODInformation;
106
- private static readonly _copiedTextures;
107
- private static copySettings;
108
- }
109
- export {};
1
+ import { BufferGeometry, Material, Mesh, Texture } from "three";
2
+ import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ export declare const EXTENSION_NAME = "NEEDLE_progressive";
4
+ declare type NEEDLE_progressive_model_LOD = {
5
+ path: string;
6
+ hash?: string;
7
+ };
8
+ /** This is the data structure we have in the NEEDLE_progressive extension */
9
+ declare type NEEDLE_progressive_model = {
10
+ guid: string;
11
+ lods: Array<NEEDLE_progressive_model_LOD>;
12
+ };
13
+ export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {};
14
+ export declare type NEEDLE_progressive_mesh_model = NEEDLE_progressive_model & {
15
+ density: number;
16
+ lods: Array<NEEDLE_progressive_model_LOD & {
17
+ density: number;
18
+ indexCount: number;
19
+ vertexCount: number;
20
+ }>;
21
+ };
22
+ /**
23
+ * This is the result of a progressive texture loading event for a material's texture slot in {@link NEEDLE_progressive.assignTextureLOD}
24
+ * @internal
25
+ */
26
+ export declare type ProgressiveMaterialTextureLoadingResult = {
27
+ /** the material the progressive texture was loaded for */
28
+ material: Material;
29
+ /** the slot in the material where the texture was loaded */
30
+ slot: string;
31
+ /** the texture that was loaded (if any) */
32
+ texture: Texture | null;
33
+ /** the level of detail that was loaded */
34
+ level: number;
35
+ };
36
+ /**
37
+ * The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
38
+ * This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
39
+ * @example
40
+ * ```javascript
41
+ * const loader = new GLTFLoader();
42
+ * loader.register(new NEEDLE_progressive());
43
+ * loader.load("model.glb", (gltf) => {
44
+ * const mesh = gltf.scene.children[0] as Mesh;
45
+ * NEEDLE_progressive.assignMeshLOD(context, sourceId, mesh, 1).then(mesh => {
46
+ * console.log("Mesh with LOD level 1 loaded", mesh);
47
+ * });
48
+ * });
49
+ * ```
50
+ */
51
+ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
52
+ /** The name of the extension */
53
+ get name(): string;
54
+ static getMeshLODInformation(geo: BufferGeometry): NEEDLE_progressive_mesh_model | null;
55
+ /** Check if a LOD level is available for a mesh or a texture
56
+ * @param obj the mesh or texture to check
57
+ * @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
58
+ * @returns true if the LOD level is available (or if any LOD level is available if level is undefined)
59
+ */
60
+ static hasLODLevelAvailable(obj: Mesh | BufferGeometry | Texture | Material, level?: number): boolean;
61
+ /** Load a different resolution of a mesh (if available)
62
+ * @param context the context
63
+ * @param source the sourceid of the file from which the mesh is loaded (this is usually the component's sourceId)
64
+ * @param mesh the mesh to load the LOD for
65
+ * @param level the level of detail to load (0 is the highest resolution)
66
+ * @returns a promise that resolves to the mesh with the requested LOD level
67
+ * @example
68
+ * ```javascript
69
+ * const mesh = this.gameObject as Mesh;
70
+ * NEEDLE_progressive.assignMeshLOD(context, sourceId, mesh, 1).then(mesh => {
71
+ * console.log("Mesh with LOD level 1 loaded", mesh);
72
+ * });
73
+ * ```
74
+ */
75
+ static assignMeshLOD(mesh: Mesh, level: number): Promise<BufferGeometry | null>;
76
+ /** Load a different resolution of a texture (if available)
77
+ * @param context the context
78
+ * @param source the sourceid of the file from which the texture is loaded (this is usually the component's sourceId)
79
+ * @param materialOrTexture the material or texture to load the LOD for (if passing in a material all textures in the material will be loaded)
80
+ * @param level the level of detail to load (0 is the highest resolution) - currently only 0 is supported
81
+ * @returns a promise that resolves to the material or texture with the requested LOD level
82
+ */
83
+ static assignTextureLOD(materialOrTexture: Material | Texture, level?: number): Promise<Array<ProgressiveMaterialTextureLoadingResult> | Texture | null>;
84
+ private static assignTextureLODForSlot;
85
+ private readonly parser;
86
+ private readonly url;
87
+ constructor(parser: GLTFParser, url: string);
88
+ afterRoot(gltf: GLTF): null;
89
+ /**
90
+ * Register a texture with LOD information
91
+ */
92
+ static registerTexture: (url: string, tex: Texture, index: number, ext: NEEDLE_progressive_texture_model) => void;
93
+ /**
94
+ * Register a mesh with LOD information
95
+ */
96
+ static registerMesh: (url: string, key: string, mesh: Mesh, level: number, index: number | undefined, ext: NEEDLE_progressive_mesh_model) => void;
97
+ /** A map of key = asset uuid and value = LOD information */
98
+ private static readonly lodInfos;
99
+ /** cache of already loaded mesh lods */
100
+ private static readonly previouslyLoaded;
101
+ /** this contains the geometry/textures that were originally loaded */
102
+ private static readonly lowresCache;
103
+ private static getOrLoadLOD;
104
+ private static assignLODInformation;
105
+ private static getAssignedLODInformation;
106
+ private static readonly _copiedTextures;
107
+ private static copySettings;
108
+ }
109
+ export {};