@needle-tools/gltf-progressive 1.1.0-alpha.1 → 1.2.0-alpha

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.
@@ -1,5 +1,5 @@
1
- var fe=Object.defineProperty,me=(t,e,r)=>e in t?fe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(me(t,typeof e!="symbol"?e+"":e,r),r);import{MeshoptDecoder as ge}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as pe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as ye}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as $,Mesh as q,Material as xe,Texture as F,TextureLoader as Le,Matrix4 as se,Frustum as De,Sphere as ve,Box3 as oe,Vector3 as P}from"three";import{GLTFLoader as Me}from"three/examples/jsm/loaders/GLTFLoader.js";let V="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",H="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(V+"draco_decoder.js",{method:"head"}).catch(t=>{V="./include/draco/",H="./include/ktx2/"});function Oe(t){V=t}function we(t){H=t}let U,Y,W;function J(t){U||(U=new pe,U.setDecoderPath(V),U.setDecoderConfig({type:"js"})),W||(W=new ye,W.setTranscoderPath(H)),Y||(Y=ge),t?W.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Q(t){t.dracoLoader||t.setDRACOLoader(U),t.ktx2Loader||t.setKTX2Loader(W),t.meshoptDecoder||t.setMeshoptDecoder(Y)}function Z(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function _e(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const o=t.substring(0,r+1);for(;o.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return o+e}return e}function ne(t){var e;return((e=t.userData)==null?void 0:e["needle:raycast-mesh"])instanceof $?t.userData["needle:raycast-mesh"]:null}function ie(t,e){(t.type==="Mesh"||t.type==="SkinnedMesh")&&(t.userData||(t.userData={}),t.userData["needle:raycast-mesh"]=e)}const C=new Array,I="NEEDLE_progressive",v=Z("debugprogressive"),ee=Symbol("needle-progressive-texture"),z=new Map,te=new Set;if(v){let t=function(){e+=1,console.log("Toggle LOD level",e,z),z.forEach((n,s)=>{for(const a of n.keys){const i=s[a];if(i.isBufferGeometry===!0){const u=w.getMeshLODInformation(i),l=u?Math.min(e,u.lods.length):0;s["DEBUG:LOD"]=e,w.assignMeshLOD(s,l),u&&(r=Math.max(r,u.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.assignTextureLOD(s,e);break}}}),e>=r&&(e=-1)},e=-1,r=2,o=!1;window.addEventListener("keyup",n=>{n.key==="p"&&t(),n.key==="w"&&(o=!o,te&&te.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=o)}))})}function ae(t,e,r){var o;if(!v)return;z.has(t)||z.set(t,{keys:[],sourceId:r});const n=z.get(t);((o=n?.keys)==null?void 0:o.includes(e))==!1&&n.keys.push(e)}const M=class{constructor(t,e){c(this,"parser"),c(this,"url"),v&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return I}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e={min:1/0,max:-1}){if(Array.isArray(t)){for(const n of t)this.getMaterialMinMaxLODsCount(n,e);return e}let r=e.min,o=e.max;if(t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const n=t;for(const s of Object.keys(n.uniforms)){const a=n.uniforms[s].value;if(a?.isTexture===!0){const i=this.getAssignedLODInformation(a);if(i){const u=this.lodInfos.get(i.key);u&&u.lods&&(r=Math.min(r,u.lods.length),o=Math.max(o,u.lods.length))}}}}else if(t.isMaterial)for(const n of Object.keys(t)){const s=t[n];if(s?.isTexture===!0){const a=this.getAssignedLODInformation(s);if(a){const i=this.lodInfos.get(a.key);i&&i.lods&&(r=Math.min(r,i.lods.length),o=Math.max(o,i.lods.length))}}}return e.min=r,e.max=o,e}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const a=t[s];if(a&&a.isTexture&&this.hasLODLevelAvailable(a,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let o,n;if(t.isMesh?o=t.geometry:(t.isBufferGeometry||t.isTexture)&&(o=t),o&&(r=o?.userData)!=null&&r.LODS){const s=o.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof q||t.isMesh===!0){const o=t.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of C)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(o,e).then(s=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(s)){const a=n.index||0;s=s[a]}s&&o!=s&&s instanceof $&&(t.geometry=s,v&&ae(t,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else v&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t instanceof xe||t.isMaterial===!0){const r=t,o=[],n=new Array;if(v&&te.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const a of Object.keys(s.uniforms)){const i=s.uniforms[a].value;if(i?.isTexture===!0){const u=this.assignTextureLODForSlot(i,e,r,a);o.push(u),n.push(a)}}}else for(const s of Object.keys(r)){const a=r[s];if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,r,s);o.push(i),n.push(s)}}return Promise.all(o).then(s=>{const a=new Array;for(let i=0;i<s.length;i++){const u=s[i],l=n[i];u&&u.isTexture===!0?a.push({material:r,slot:l,texture:u,level:e}):a.push({material:r,slot:l,texture:null,level:e})}return a})}if(t instanceof F||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(o==="glyphMap")return Promise.resolve(t);const n="LOD:requested level:"+o;return r&&(r[n]=e),M.getOrLoadLOD(t,e).then(s=>{if(r&&r[n]!=e||Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t&&(r&&o&&(r[o]=s),v&&o&&r)){const a=this.getAssignedLODInformation(t);a&&ae(r,o,a.url)}return s}else v=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return v&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[I];if(s){let a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===n&&(a=!0,M.registerTexture(this.url,i,n,s));a||this.parser.getDependency("texture",n).then(i=>{i&&M.registerTexture(this.url,i,n,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[I];if(s&&s.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===n&&M.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var r,o,n,s;const a=v=="verbose",i=t.userData.LODS;if(!i)return null;const u=i?.key;let l;if(t.isTexture===!0){const x=t;x.source&&x.source[ee]&&(l=x.source[ee])}if(l||(l=M.lodInfos.get(u)),l){if(e>0){let f=!1;const b=Array.isArray(l.lods);if(b&&e>=l.lods.length?f=!0:b||(f=!0),f)return this.lowresCache.get(u)}const x=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!x)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=_e(i.url,x);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=h+"_"+l.guid,b=this.previouslyLoaded.get(f);if(b!==void 0){a&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let p=await b.catch(G=>(console.error(`Error loading LOD ${e} from ${h}
2
- `,G),null)),_=!1;if(p==null||(p instanceof F&&t instanceof F?(o=p.image)!=null&&o.data||(n=p.source)!=null&&n.data?p=this.copySettings(t,p):(_=!0,this.previouslyLoaded.delete(f)):p instanceof $&&t instanceof $&&((s=p.attributes.position)!=null&&s.array||(_=!0,this.previouslyLoaded.delete(f)))),!_)return p}const m=l,k=new Promise(async(p,_)=>{const G=new Me;Q(G),v&&(await new Promise(y=>setTimeout(y,1e3)),a&&console.warn("Start loading (delayed) "+h,m.guid));let N=h;if(m&&Array.isArray(m.lods)){const y=m.lods[e];y.hash&&(N+="?v="+y.hash)}const g=await G.loadAsync(N).catch(y=>(console.error(`Error loading LOD ${e} from ${h}
3
- `,y),null));if(!g)return null;const L=g.parser;a&&console.log("Loading finished "+h,m.guid);let D=0;if(g.parser.json.textures){let y=!1;for(const d of g.parser.json.textures){if(d!=null&&d.extensions){const O=d?.extensions[I];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){let d=await L.getDependency("texture",D);return d&&M.assignLODInformation(i.url,d,u,e,void 0,void 0),a&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',h,D,d,f),t instanceof F&&(d=this.copySettings(t,d)),d&&(d.guid=m.guid),p(d)}else v&&console.warn("Could not find texture with guid",m.guid)}if(D=0,g.parser.json.meshes){let y=!1;for(const d of g.parser.json.meshes){if(d!=null&&d.extensions){const O=d?.extensions[I];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){const d=await L.getDependency("mesh",D),O=m;if(a&&console.log(`Loaded Mesh "${d.name}"`,h,D,d,f),d.isMesh===!0){const T=d.geometry;return M.assignLODInformation(i.url,T,u,e,void 0,O.density),p(T)}else{const T=new Array;for(let B=0;B<d.children.length;B++){const j=d.children[B];if(j instanceof q){const R=j.geometry;M.assignLODInformation(i.url,R,u,e,B,O.density),T.push(R)}}return p(T)}}}return p(null)});return this.previouslyLoaded.set(f,k),await k}else if(t instanceof F){a&&console.log("Load texture from uri: "+h);const f=await new Le().loadAsync(h);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,a&&console.log(l,f)):v&&console.warn("failed loading",h),f}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,o,n,s){if(!e)return;e.userData||(e.userData={});const a=new be(t,r,o,n,s);e.userData.LODS=a,e.userData.LOD=o}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),v&&console.warn(`Copying texture settings
1
+ var fe=Object.defineProperty,ge=(t,e,r)=>e in t?fe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(ge(t,typeof e!="symbol"?e+"":e,r),r);import{MeshoptDecoder as me}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as pe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as xe}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as q,Mesh as V,Material as ye,Texture as $,TextureLoader as Le,Matrix4 as se,Frustum as ve,Sphere as De,Box3 as oe,Vector3 as B}from"three";import{GLTFLoader as Me}from"three/examples/jsm/loaders/GLTFLoader.js";let X="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",Y="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(X+"draco_decoder.js",{method:"head"}).catch(t=>{X="./include/draco/",Y="./include/ktx2/"});function Oe(t){X=t}function _e(t){Y=t}let z,J,W;function Q(t){z||(z=new pe,z.setDecoderPath(X),z.setDecoderConfig({type:"js"})),W||(W=new xe,W.setTranscoderPath(Y)),J||(J=me),t?W.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Z(t){t.dracoLoader||t.setDRACOLoader(z),t.ktx2Loader||t.setKTX2Loader(W),t.meshoptDecoder||t.setMeshoptDecoder(J)}function ee(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function we(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const n=t.substring(0,r+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}function ne(t){var e;return((e=t.userData)==null?void 0:e["needle:raycast-mesh"])instanceof q?t.userData["needle:raycast-mesh"]:null}function ie(t,e){(t.type==="Mesh"||t.type==="SkinnedMesh")&&(t.userData||(t.userData={}),t.userData["needle:raycast-mesh"]=e)}const k=new Array,P="NEEDLE_progressive",L=ee("debugprogressive"),te=Symbol("needle-progressive-texture"),U=new Map,re=new Set;if(L){let t=function(){e+=1,console.log("Toggle LOD level",e,U),U.forEach((o,s)=>{for(const i of o.keys){const a=s[i];if(a.isBufferGeometry===!0){const u=_.getMeshLODInformation(a),l=u?Math.min(e,u.lods.length):0;s["DEBUG:LOD"]=e,_.assignMeshLOD(s,l),u&&(r=Math.max(r,u.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,_.assignTextureLOD(s,e);break}}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",o=>{o.key==="p"&&t(),o.key==="w"&&(n=!n,re&&re.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=n)}))})}function ae(t,e,r){var n;if(!L)return;U.has(t)||U.set(t,{keys:[],sourceId:r});const o=U.get(t);((n=o?.keys)==null?void 0:n.includes(e))==!1&&o.keys.push(e)}const M=class{constructor(t,e){c(this,"parser"),c(this,"url"),c(this,"_isLoadingMesh"),c(this,"loadMesh",r=>{var n,o;if(this._isLoadingMesh)return null;const s=(o=(n=this.parser.json.meshes[r])==null?void 0:n.extensions)==null?void 0:o[P];return s?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>(this._isLoadingMesh=!1,i&&M.registerMesh(this.url,s.guid,i,s.lods.length,void 0,s),i))):null}),L&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return P}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const r="LODS:minmax",n=t[r];if(n!=null)return n;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const s of t)this.getMaterialMinMaxLODsCount(s,e);return t[r]=e,e}L==="verbose"&&console.log("getMaterialMinMaxLODsCount",t);const o=s=>{const i=this.getAssignedLODInformation(s);if(i){const a=this.lodInfos.get(i.key);if(a&&a.lods){e.min_count=Math.min(e.min_count,a.lods.length),e.max_count=Math.max(e.max_count,a.lods.length);for(let u=0;u<a.lods.length;u++){const l=a.lods[u];l.width&&(e.lods[u]=e.lods[u]||{min_height:1/0,max_height:0},e.lods[u].min_height=Math.min(e.lods[u].min_height,l.height),e.lods[u].max_height=Math.max(e.lods[u].max_height,l.height))}}}};if(t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const s=t;for(const i of Object.keys(s.uniforms)){const a=s.uniforms[i].value;a?.isTexture===!0&&o(a)}}else if(t.isMaterial)for(const s of Object.keys(t)){const i=t[s];i?.isTexture===!0&&o(i)}return t[r]=e,e}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const i=t[s];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let n,o;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(r=n?.userData)!=null&&r.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(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof V||t.isMesh===!0){const n=t.geometry,o=this.getAssignedLODInformation(n);if(!o)return Promise.resolve(null);for(const s of k)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(n,e).then(s=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(s)){const i=o.index||0;s=s[i]}s&&n!=s&&s instanceof q&&(t.geometry=s,L&&ae(t,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else L&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t instanceof ye||t.isMaterial===!0){const r=t,n=[],o=new Array;if(L&&re.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const i of Object.keys(s.uniforms)){const a=s.uniforms[i].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,i);n.push(u),o.push(i)}}}else for(const s of Object.keys(r)){const i=r[s];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,r,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 u=s[a],l=o[a];u&&u.isTexture===!0?i.push({material:r,slot:l,texture:u,level:e}):i.push({material:r,slot:l,texture:null,level:e})}return i})}if(t instanceof $||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):M.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return null;if(o?.isTexture===!0){if(o!=t){if(r&&n){const s=r[n];if(s){const i=this.getAssignedLODInformation(s);if(i&&i?.level<e)return L==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,s,o),null}r[n]=o}if(L&&n&&r){const s=this.getAssignedLODInformation(t);s&&ae(r,n,s.url)}}return o}else L=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}afterRoot(t){var e,r;return L&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[P];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,M.registerTexture(this.url,a,s.lods.length,o,s));i||this.parser.getDependency("texture",o).then(a=>{a&&M.registerTexture(this.url,a,s.lods.length,o,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[P];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&&M.registerMesh(this.url,s.guid,i,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var r,n,o,s;const i=L=="verbose",a=t.userData.LODS;if(!a)return null;const u=a?.key;let l;if(t.isTexture===!0){const y=t;y.source&&y.source[te]&&(l=y.source[te])}if(l||(l=M.lodInfos.get(u)),l){if(e>0){let f=!1;const w=Array.isArray(l.lods);if(w&&e>=l.lods.length?f=!0:w||(f=!0),f)return this.lowresCache.get(u)}const y=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!y)return L&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=we(a.url,y);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=h+"_"+l.guid,w=this.previouslyLoaded.get(f);if(w!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let m=await w.catch(G=>(console.error(`Error loading LOD ${e} from ${h}
2
+ `,G),null)),b=!1;if(m==null||(m instanceof $&&t instanceof $?(n=m.image)!=null&&n.data||(o=m.source)!=null&&o.data?m=this.copySettings(t,m):(b=!0,this.previouslyLoaded.delete(f)):m instanceof q&&t instanceof q&&((s=m.attributes.position)!=null&&s.array||(b=!0,this.previouslyLoaded.delete(f)))),!b)return m}const g=l,S=new Promise(async(m,b)=>{const G=new Me;Z(G),L&&(await new Promise(x=>setTimeout(x,1e3)),i&&console.warn("Start loading (delayed) "+h,g.guid));let N=h;if(g&&Array.isArray(g.lods)){const x=g.lods[e];x.hash&&(N+="?v="+x.hash)}const p=await G.loadAsync(N).catch(x=>(console.error(`Error loading LOD ${e} from ${h}
3
+ `,x),null));if(!p)return null;const v=p.parser;i&&console.log("Loading finished "+h,g.guid);let D=0;if(p.parser.json.textures){let x=!1;for(const d of p.parser.json.textures){if(d!=null&&d.extensions){const O=d?.extensions[P];if(O!=null&&O.guid&&O.guid===g.guid){x=!0;break}}D++}if(x){let d=await v.getDependency("texture",D);return d&&M.assignLODInformation(a.url,d,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',h,D,d,f),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=g.guid),m(d)}else L&&console.warn("Could not find texture with guid",g.guid)}if(D=0,p.parser.json.meshes){let x=!1;for(const d of p.parser.json.meshes){if(d!=null&&d.extensions){const O=d?.extensions[P];if(O!=null&&O.guid&&O.guid===g.guid){x=!0;break}}D++}if(x){const d=await v.getDependency("mesh",D),O=g;if(i&&console.log(`Loaded Mesh "${d.name}"`,h,D,d,f),d.isMesh===!0){const A=d.geometry;return M.assignLODInformation(a.url,A,u,e,void 0,O.density),m(A)}else{const A=new Array;for(let C=0;C<d.children.length;C++){const j=d.children[C];if(j instanceof V){const R=j.geometry;M.assignLODInformation(a.url,R,u,e,C,O.density),A.push(R)}}return m(A)}}}return m(null)});return this.previouslyLoaded.set(f,S),await S}else if(t instanceof $){i&&console.log("Load texture from uri: "+h);const f=await new Le().loadAsync(h);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,i&&console.log(l,f)):L&&console.warn("failed loading",h),f}}else L&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,n,o,s){if(!e)return;e.userData||(e.userData={});const i=new be(t,r,n,o,s);e.userData.LODS=i,e.userData.LOD=n}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),L&&console.warn(`Copying texture settings
4
4
  `,t.uuid,`
5
- `,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let w=M;c(w,"registerTexture",(t,e,r,o)=>{v&&console.log("> Progressive: register texture",r,e.name,e.uuid,e,o),e.source&&(e.source[ee]=o);const n=o.guid;M.assignLODInformation(t,e,n,0,0,void 0),M.lodInfos.set(n,o),M.lowresCache.set(n,e)}),c(w,"registerMesh",(t,e,r,o,n,s)=>{var a;v&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const i=r.geometry;i.userData||(i.userData={}),M.assignLODInformation(t,i,e,o,n,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),o>0&&!ne(r)&&ie(r,i);for(const l of C)(a=l.onRegisteredNewMesh)==null||a.call(l,r,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class be{constructor(e,r,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}const re=Z("debugprogressive"),Se=Z("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},S=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new De),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new ve),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new P),c(this,"_tempBoxSize",new P),c(this,"_tempBox2Size",new P),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){C.push(t)}static removePlugin(t){const e=C.indexOf(t);e>=0&&C.splice(e,1)}static get(t){return t[le]?t[le]:new S(t)}get plugins(){return C}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;J(this.renderer),this.renderer.render=function(r,o){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const n=e._frame,s=t++;e.onBeforeRender(r,o,s,n),e._originalRender.call(this,r,o),e.onAfterRender(r,o,s,n)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,o){}onAfterRender(t,e,r,o){var n,s;if(this.pause)return;const a=this.renderer.renderLists.get(t,0),i=a.opaque;let u=!0;if(i.length===1){const l=i[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of i){if(h.material&&(((n=h.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){re&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}const x=a.transparent;for(const h of x){const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}}}updateLODs(t,e,r,o,n){var s,a;let i=r.userData.LOD_state;if(i||(i=new Te,r.userData.LOD_state=i),i.frames++<2)return;for(const l of C)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,o,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of C)(a=l.onAfterUpdatedLOD)==null||a.call(l,this.renderer,t,e,r,E);i.lastLodLevel_Mesh=E.mesh_lod,i.lasLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}const r=t;let o=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(o=!0),o&&(r.NEEDLE_LOD=e,w.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return w.assignMeshLOD(t,e).then(o=>(o&&t.userData.LOD==e&&r!=t.geometry,o))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,o=t.max,n=(r.x+o.x)*.5,s=(r.y+o.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,o,n){var s;if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1;if(re&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=w.getMeshLODInformation(e.geometry),u=i?.lods,l=u&&u.length>0,x=w.getMaterialMinMaxLODsCount(e.material),h=x?.min!=1/0&&x.min>0&&x.max>0;if(!l&&!h){n.mesh_lod=0,n.texture_lod=0;return}if(l||(a=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){n.mesh_lod=99,n.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const b=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),S.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&b.fov>70){const g=this._tempBox.min,L=this._tempBox.max;let D=g.x,y=g.y,d=L.x,O=L.y;const T=2,B=1.5,j=(g.x+L.x)*.5,R=(g.y+L.y)*.5;D=(D-j)*T+j,y=(y-R)*T+R,d=(d-j)*T+j,O=(O-R)*T+R;const de=D<0&&d>0?0:Math.min(Math.abs(g.x),Math.abs(L.x)),he=y<0&&O>0?0:Math.min(Math.abs(g.y),Math.abs(L.y)),K=Math.max(de,he);r.lastCentrality=(B-K)*(B-K)*(B-K)}else r.lastCentrality=1;const m=this._tempBox.getSize(this._tempBoxSize);m.multiplyScalar(.5),screen.availHeight>0&&m.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),m.x*=b.aspect;const k=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(k);const _=p.getSize(this._tempBox2Size),G=Math.max(_.x,_.y);if(Math.max(m.x,m.y)!=0&&G!=0&&(m.z=_.z/Math.max(_.x,_.y)*Math.max(m.x,m.y)),r.lastScreenCoverage=Math.max(m.x,m.y,m.z),r.lastScreenspaceVolume.copy(m),r.lastScreenCoverage*=r.lastCentrality,re&&S.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const L=S.corner0,D=S.corner1,y=S.corner2,d=S.corner3;L.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=L.x,y.copy(this._tempBox.max),y.y=L.y,d.copy(this._tempBox.max);const O=(L.z+d.z)*.5;L.z=D.z=y.z=d.z=O,L.applyMatrix4(g),D.applyMatrix4(g),y.applyMatrix4(g),d.applyMatrix4(g),S.debugDrawLine(L,D,255),S.debugDrawLine(L,y,255),S.debugDrawLine(D,d,255),S.debugDrawLine(y,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let g=0;g<u.length;g++)if(u[g].density/r.lastScreenCoverage<o){N=g;break}}N<a&&(a=N)}if(n.mesh_lod=a,x!=null&&x.min&&x.min>0&&x.max>0){const b=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=Ee(x.max,0,b)}else n.texture_lod=0}};let A=S;c(A,"debugDrawLine"),c(A,"corner0",new P),c(A,"corner1",new P),c(A,"corner2",new P),c(A,"corner3",new P),c(A,"_tempPtInside",new P);function Ee(t,e,r){return t+(e-t)*r}class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",0),c(this,"lasLodLevel_Texture",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new P),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let o=t;o!=null;o=Object.getPrototypeOf(o)){const n=Object.getOwnPropertySymbols(o),s=n.find(i=>i.toString()=="Symbol(renderer)"),a=n.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=t[s].threeRenderer),!r&&a!=null&&(r=t[a])}if(e){console.log("Adding Needle LODs to modelviewer");const o=A.get(e);if(A.addPlugin(new Ae(t)),o.enable(),r){const n=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];n&&e.render(r,n)}return()=>{o.disable()}}return null}class Ae{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,o,n){this.tryParseMeshLOD(r,n),this.tryParseTextureLOD(r,n)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[X]==!0)return;r[X]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&r.material){let s=function(i){var u,l,x;if(i[X]==!0)return;i[X]=!0,i.userData&&(i.userData.LOD=-1);const h=Object.keys(i);for(let f=0;f<h.length;f++){const b=h[f],m=i[b];if(m?.isTexture===!0){const k=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=o.parser.json.textures[k];if(!p){console.warn("Texture data not found for texture index "+k);continue}if((x=p?.extensions)!=null&&x[I]){const _=p.extensions[I];_&&n&&w.registerTexture(n,m,_.lods.length,_)}}}};const a=r.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,r){var o,n;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const a=(n=(o=r.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[I];if(a&&s){const i=r.uuid;w.registerMesh(s,i,r,0,a.lods.length,a)}}}function Pe(t,e,r,o){J(e),Q(r),r.register(s=>new w(s,t));const n=A.get(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{I as EXTENSION_NAME,A as LODsManager,w as NEEDLE_progressive,Q as addDracoAndKTX2Loaders,J as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,we as setKTX2TranscoderLocation,ie as setRaycastMesh,Pe as useNeedleProgressive};
5
+ `,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let _=M;c(_,"registerTexture",(t,e,r,n,o)=>{L&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,o),e.source&&(e.source[te]=o);const s=o.guid;M.assignLODInformation(t,e,s,r,n,void 0),M.lodInfos.set(s,o),M.lowresCache.set(s,e)}),c(_,"registerMesh",(t,e,r,n,o,s)=>{var i;L&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const a=r.geometry;a.userData||(a.userData={}),M.assignLODInformation(t,a,e,n,o,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),n>0&&!ne(r)&&ie(r,a);for(const l of k)(i=l.onRegisteredNewMesh)==null||i.call(l,r,s)}),c(_,"lodInfos",new Map),c(_,"previouslyLoaded",new Map),c(_,"lowresCache",new Map);class be{constructor(e,r,n,o,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=n,o!=null&&(this.index=o),s!=null&&(this.density=s)}}const F=ee("debugprogressive"),Se=ee("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},T=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new ve),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new De),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new B),c(this,"_tempBoxSize",new B),c(this,"_tempBox2Size",new B),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){k.push(t)}static removePlugin(t){const e=k.indexOf(t);e>=0&&k.splice(e,1)}static get(t){return t[le]?t[le]:new T(t)}get plugins(){return k}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;Q(this.renderer),this.renderer.render=function(r,n){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const o=e._frame,s=t++;e.onBeforeRender(r,n,s,o),e._originalRender.call(this,r,n),e.onAfterRender(r,n,s,o)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,n){}onAfterRender(t,e,r,n){var o,s;if(this.pause)return;const i=this.renderer.renderLists.get(t,0),a=i.opaque;let u=!0;if(a.length===1){const l=a[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&n%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of a){if(h.material&&(((o=h.geometry)==null?void 0:o.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){F&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}const y=i.transparent;for(const h of y){const f=h.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}}}updateLODs(t,e,r,n,o){var s,i;let a=r.userData.LOD_state;if(a||(a=new Te,r.userData.LOD_state=a),a.frames++<2)return;for(const l of k)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,a,n,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of k)(i=l.onAfterUpdatedLOD)==null||i.call(l,this.renderer,t,e,r,E);a.lastLodLevel_Mesh=E.mesh_lod,a.lastLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}const r=t;let n=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(n=!0),n&&(r.NEEDLE_LOD=e,_.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return _.assignMeshLOD(t,e).then(n=>(n&&t.userData.LOD==e&&r!=t.geometry,n))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,n=t.max,o=(r.x+n.x)*.5,s=(r.y+n.y)*.5;return this._tempPtInside.set(o,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,n,o){var s;if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=10+1;if(F&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=_.getMeshLODInformation(e.geometry),u=a?.lods,l=u&&u.length>0,y=_.getMaterialMinMaxLODsCount(e.material),h=y?.min_count!=1/0&&y.min_count>0&&y.max_count>0;if(!l&&!h){o.mesh_lod=0,o.texture_lod=0;return}if(l||(i=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){o.mesh_lod=99,o.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const w=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const p=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(p)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),T.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&w.fov>70){const p=this._tempBox.min,v=this._tempBox.max;let D=p.x,x=p.y,d=v.x,O=v.y;const A=2,C=1.5,j=(p.x+v.x)*.5,R=(p.y+v.y)*.5;D=(D-j)*A+j,x=(x-R)*A+R,d=(d-j)*A+j,O=(O-R)*A+R;const de=D<0&&d>0?0:Math.min(Math.abs(p.x),Math.abs(v.x)),he=x<0&&O>0?0:Math.min(Math.abs(p.y),Math.abs(v.y)),H=Math.max(de,he);r.lastCentrality=(C-H)*(C-H)*(C-H)}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*=w.aspect;const S=t.matrixWorldInverse,m=this._tempBox2;m.copy(f),m.applyMatrix4(e.matrixWorld),m.applyMatrix4(S);const b=m.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,F&&T.debugDrawLine){const p=this.tempMatrix.copy(this.projectionScreenMatrix);p.invert();const v=T.corner0,D=T.corner1,x=T.corner2,d=T.corner3;v.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=v.x,x.copy(this._tempBox.max),x.y=v.y,d.copy(this._tempBox.max);const O=(v.z+d.z)*.5;v.z=D.z=x.z=d.z=O,v.applyMatrix4(p),D.applyMatrix4(p),x.applyMatrix4(p),d.applyMatrix4(p),T.debugDrawLine(v,D,255),T.debugDrawLine(v,x,255),T.debugDrawLine(D,d,255),T.debugDrawLine(x,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let p=0;p<u.length;p++)if(u[p].density/r.lastScreenCoverage<n){N=p;break}}N<i&&(i=N)}if(o.mesh_lod=i,h)if(r.lastLodLevel_Texture<0){if(o.texture_lod=y.max_count-1,F){const w=y.lods[y.max_count-1];F&&console.log(`First Texture LOD ${o.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenCoverage*1.5,g=this.renderer.domElement.clientHeight/window.devicePixelRatio*w;for(let S=y.lods.length-1;S>=0;S--){const m=y.lods[S];if(m.max_height>g){o.texture_lod=S,o.texture_lod<r.lastLodLevel_Texture&&F&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} (${m.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${g.toFixed(0)}px) - ${e.name}`);break}}}else o.texture_lod=0}};let I=T;c(I,"debugDrawLine"),c(I,"corner0",new B),c(I,"corner1",new B),c(I,"corner2",new B),c(I,"corner3",new B),c(I,"_tempPtInside",new B);class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",-1),c(this,"lastLodLevel_Texture",-1),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new B),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),K=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let n=t;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=t[s].threeRenderer),!r&&i!=null&&(r=t[i])}if(e){console.log("Adding Needle LODs to modelviewer");const n=I.get(e);if(I.addPlugin(new Ee(t)),n.enable(),r){const o=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];o&&e.render(r,o)}return()=>{n.disable()}}return null}class Ee{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,n,o){this.tryParseMeshLOD(r,o),this.tryParseTextureLOD(r,o)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[K]==!0)return;r[K]=!0;const n=this.tryGetCurrentGLTF(e),o=this.getUrl();if(o&&n&&r.material){let s=function(a){var u,l,y;if(a[K]==!0)return;a[K]=!0,a.userData&&(a.userData.LOD=-1);const h=Object.keys(a);for(let f=0;f<h.length;f++){const w=h[f],g=a[w];if(g?.isTexture===!0){const S=(l=(u=g.userData)==null?void 0:u.associations)==null?void 0:l.textures,m=n.parser.json.textures[S];if(!m){console.warn("Texture data not found for texture index "+S);continue}if((y=m?.extensions)!=null&&y[P]){const b=m.extensions[P];b&&o&&_.registerTexture(o,g,b.lods.length,S,b)}}}};const i=r.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}tryParseMeshLOD(e,r){var n,o;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const i=(o=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[P];if(i&&s){const a=r.uuid;_.registerMesh(s,a,r,0,i.lods.length,i)}}}function Ae(t,e,r,n){Q(e),Z(r),r.register(s=>new _(s,t));const o=I.get(e);return n?.enableLODsManager!==!1&&o.enable(),o}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,I as LODsManager,_ as NEEDLE_progressive,Z as addDracoAndKTX2Loaders,Q as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,_e as setKTX2TranscoderLocation,ie as setRaycastMesh,Ae as useNeedleProgressive};
@@ -1,5 +1,5 @@
1
- "use strict";var de=Object.defineProperty;var he=(a,e,t)=>e in a?de(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var u=(a,e,t)=>(he(a,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ge=require("three/examples/jsm/libs/meshopt_decoder.module.js"),pe=require("three/examples/jsm/loaders/DRACOLoader.js"),ye=require("three/examples/jsm/loaders/KTX2Loader.js"),g=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js");let $="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ee="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch($+"draco_decoder.js",{method:"head"}).catch(a=>{$="./include/draco/",ee="./include/ktx2/"});function Le(a){$=a}function xe(a){ee=a}let U,Z,z;function te(a){U||(U=new pe.DRACOLoader,U.setDecoderPath($),U.setDecoderConfig({type:"js"})),z||(z=new ye.KTX2Loader,z.setTranscoderPath(ee)),Z||(Z=ge.MeshoptDecoder),a?z.detectSupport(a):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function re(a){a.dracoLoader||a.setDRACOLoader(U),a.ktx2Loader||a.setKTX2Loader(z),a.meshoptDecoder||a.setMeshoptDecoder(Z)}function se(a){const t=new URL(window.location.href).searchParams.get(a);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function De(a,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||a===void 0)return e;const t=a.lastIndexOf("/");if(t>=0){const r=a.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}function ae(a){var e;return((e=a.userData)==null?void 0:e["needle:raycast-mesh"])instanceof g.BufferGeometry?a.userData["needle:raycast-mesh"]:null}function le(a,e){(a.type==="Mesh"||a.type==="SkinnedMesh")&&(a.userData||(a.userData={}),a.userData["needle:raycast-mesh"]=e)}const R=new Array,k="NEEDLE_progressive",_=se("debugprogressive"),J=Symbol("needle-progressive-texture"),W=new Map,j=new Set;if(_){let a=function(){e+=1,console.log("Toggle LOD level",e,W),W.forEach((i,n)=>{for(const s of i.keys){const o=n[s];if(o.isBufferGeometry===!0){const l=S.getMeshLODInformation(o),d=l?Math.min(e,l.lods.length):0;n["DEBUG:LOD"]=e,S.assignMeshLOD(n,d),l&&(t=Math.max(t,l.lods.length-1))}else if(n.isMaterial===!0){n["DEBUG:LOD"]=e,S.assignTextureLOD(n,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&a(),i.key==="w"&&(r=!r,j&&j.forEach(n=>{n.name!="BackgroundCubeMaterial"&&"wireframe"in n&&(n.wireframe=r)}))})}function ie(a,e,t){var i;if(!_)return;W.has(a)||W.set(a,{keys:[],sourceId:t});const r=W.get(a);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const v=class{constructor(e,t){u(this,"parser");u(this,"url");_&&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 getMaterialMinMaxLODsCount(e,t={min:1/0,max:-1}){if(Array.isArray(e)){for(const n of e)this.getMaterialMinMaxLODsCount(n,t);return t}let r=t.min,i=t.max;if(e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const n=e;for(const s of Object.keys(n.uniforms)){const o=n.uniforms[s].value;if((o==null?void 0:o.isTexture)===!0){const l=this.getAssignedLODInformation(o);if(l){const d=this.lodInfos.get(l.key);d&&d.lods&&(r=Math.min(r,d.lods.length),i=Math.max(i,d.lods.length))}}}}else if(e.isMaterial)for(const n of Object.keys(e)){const s=e[n];if((s==null?void 0:s.isTexture)===!0){const o=this.getAssignedLODInformation(s);if(o){const l=this.lodInfos.get(o.key);l&&l.lods&&(r=Math.min(r,l.lods.length),i=Math.max(i,l.lods.length))}}}return t.min=r,t.max=i,t}static hasLODLevelAvailable(e,t){var n;if(Array.isArray(e)){for(const s of e)if(this.hasLODLevelAvailable(s,t))return!0;return!1}if(e.isMaterial===!0){for(const s of Object.keys(e)){const o=e[s];if(o&&o.isTexture&&this.hasLODLevelAvailable(o,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&&(n=r==null?void 0:r.userData)!=null&&n.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 g.Mesh||e.isMesh===!0){const i=e.geometry,n=this.getAssignedLODInformation(i);if(!n)return Promise.resolve(null);for(const s of R)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,v.getOrLoadLOD(i,t).then(s=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(s)){const o=n.index||0;s=s[o]}s&&i!=s&&s instanceof g.BufferGeometry&&(e.geometry=s,_&&ie(e,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else _&&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 g.Material||e.isMaterial===!0){const r=e,i=[],n=new Array;if(_&&j.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const o of Object.keys(s.uniforms)){const l=s.uniforms[o].value;if((l==null?void 0:l.isTexture)===!0){const d=this.assignTextureLODForSlot(l,t,r,o);i.push(d),n.push(o)}}}else for(const s of Object.keys(r)){const o=r[s];if((o==null?void 0:o.isTexture)===!0){const l=this.assignTextureLODForSlot(o,t,r,s);i.push(l),n.push(s)}}return Promise.all(i).then(s=>{const o=new Array;for(let l=0;l<s.length;l++){const d=s[l],h=n[l];d&&d.isTexture===!0?o.push({material:r,slot:h,texture:d,level:t}):o.push({material:r,slot:h,texture:null,level:t})}return o})}if(e instanceof g.Texture||e.isTexture===!0){const r=e;return this.assignTextureLODForSlot(r,t,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(e,t,r,i){if((e==null?void 0:e.isTexture)!==!0)return Promise.resolve(null);if(i==="glyphMap")return Promise.resolve(e);const n="LOD:requested level:"+i;return r&&(r[n]=t),v.getOrLoadLOD(e,t).then(s=>{if(r&&r[n]!=t||Array.isArray(s))return null;if((s==null?void 0:s.isTexture)===!0){if(s!=e&&(r&&i&&(r[i]=s),_&&i&&r)){const o=this.getAssignedLODInformation(e);o&&ie(r,i,o.url)}return s}else _=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(s=>(console.error("Error loading LOD",e,s),null))}afterRoot(e){var t,r;return _&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,n)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s){let o=!1;for(const l of this.parser.associations.keys())l.isTexture===!0&&this.parser.associations.get(l).textures===n&&(o=!0,v.registerTexture(this.url,l,n,s));o||this.parser.getDependency("texture",n).then(l=>{l&&v.registerTexture(this.url,l,n,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,n)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const l=this.parser.associations.get(o);l.meshes===n&&v.registerMesh(this.url,s.guid,o,s.lods.length,l.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var o,l,d,h;const r=_=="verbose",i=e.userData.LODS;if(!i)return null;const n=i==null?void 0:i.key;let s;if(e.isTexture===!0){const L=e;L.source&&L.source[J]&&(s=L.source[J])}if(s||(s=v.lodInfos.get(n)),s){if(t>0){let m=!1;const D=Array.isArray(s.lods);if(D&&t>=s.lods.length?m=!0:D||(m=!0),m)return this.lowresCache.get(n)}const L=Array.isArray(s.lods)?(o=s.lods[t])==null?void 0:o.path:s.lods;if(!L)return _&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const f=De(i.url,L);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const m=f+"_"+s.guid,D=this.previouslyLoaded.get(m);if(D!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${m}`);let w=await D.catch(F=>(console.error(`Error loading LOD ${t} from ${f}
2
- `,F),null)),E=!1;if(w==null||(w instanceof g.Texture&&e instanceof g.Texture?(l=w.image)!=null&&l.data||(d=w.source)!=null&&d.data?w=this.copySettings(e,w):(E=!0,this.previouslyLoaded.delete(m)):w instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((h=w.attributes.position)!=null&&h.array||(E=!0,this.previouslyLoaded.delete(m)))),!E)return w}const O=s,M=new Promise(async(w,E)=>{const F=new me.GLTFLoader;re(F),_&&(await new Promise(p=>setTimeout(p,1e3)),r&&console.warn("Start loading (delayed) "+f,O.guid));let K=f;if(O&&Array.isArray(O.lods)){const p=O.lods[t];p.hash&&(K+="?v="+p.hash)}const B=await F.loadAsync(K).catch(p=>(console.error(`Error loading LOD ${t} from ${f}
3
- `,p),null));if(!B)return null;const Y=B.parser;r&&console.log("Loading finished "+f,O.guid);let y=0;if(B.parser.json.textures){let p=!1;for(const c of B.parser.json.textures){if(c!=null&&c.extensions){const x=c==null?void 0:c.extensions[k];if(x!=null&&x.guid&&x.guid===O.guid){p=!0;break}}y++}if(p){let c=await Y.getDependency("texture",y);return c&&v.assignLODInformation(i.url,c,n,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+c.name+'"',f,y,c,m),e instanceof g.Texture&&(c=this.copySettings(e,c)),c&&(c.guid=O.guid),w(c)}else _&&console.warn("Could not find texture with guid",O.guid)}if(y=0,B.parser.json.meshes){let p=!1;for(const c of B.parser.json.meshes){if(c!=null&&c.extensions){const x=c==null?void 0:c.extensions[k];if(x!=null&&x.guid&&x.guid===O.guid){p=!0;break}}y++}if(p){const c=await Y.getDependency("mesh",y),x=O;if(r&&console.log(`Loaded Mesh "${c.name}"`,f,y,c,m),c.isMesh===!0){const T=c.geometry;return v.assignLODInformation(i.url,T,n,t,void 0,x.density),w(T)}else{const T=new Array;for(let C=0;C<c.children.length;C++){const I=c.children[C];if(I instanceof g.Mesh){const N=I.geometry;v.assignLODInformation(i.url,N,n,t,C,x.density),T.push(N)}}return w(T)}}}return w(null)});return this.previouslyLoaded.set(m,M),await M}else if(e instanceof g.Texture){r&&console.log("Load texture from uri: "+f);const D=await new g.TextureLoader().loadAsync(f);return D?(D.guid=s.guid,D.flipY=!1,D.needsUpdate=!0,D.colorSpace=e.colorSpace,r&&console.log(s,D)):_&&console.warn("failed loading",f),D}}else _&&console.warn(`Can not load LOD ${t}: no LOD info found for "${n}" ${e.name}`,e.type);return null}static assignLODInformation(e,t,r,i,n,s){if(!t)return;t.userData||(t.userData={});const o=new Me(e,r,i,n,s);t.userData.LODS=o,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){return t=t.clone(),_&&console.warn(`Copying texture settings
1
+ "use strict";var fe=Object.defineProperty;var he=(l,e,t)=>e in l?fe(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var c=(l,e,t)=>(he(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ge=require("three/examples/jsm/libs/meshopt_decoder.module.js"),pe=require("three/examples/jsm/loaders/DRACOLoader.js"),ye=require("three/examples/jsm/loaders/KTX2Loader.js"),g=require("three"),Le=require("three/examples/jsm/loaders/GLTFLoader.js");let X="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ee="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(X+"draco_decoder.js",{method:"head"}).catch(l=>{X="./include/draco/",ee="./include/ktx2/"});function xe(l){X=l}function me(l){ee=l}let $,Z,U;function te(l){$||($=new pe.DRACOLoader,$.setDecoderPath(X),$.setDecoderConfig({type:"js"})),U||(U=new ye.KTX2Loader,U.setTranscoderPath(ee)),Z||(Z=ge.MeshoptDecoder),l?U.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function re(l){l.dracoLoader||l.setDRACOLoader($),l.ktx2Loader||l.setKTX2Loader(U),l.meshoptDecoder||l.setMeshoptDecoder(Z)}function se(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function De(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}function ae(l){var e;return((e=l.userData)==null?void 0:e["needle:raycast-mesh"])instanceof g.BufferGeometry?l.userData["needle:raycast-mesh"]:null}function le(l,e){(l.type==="Mesh"||l.type==="SkinnedMesh")&&(l.userData||(l.userData={}),l.userData["needle:raycast-mesh"]=e)}const I=new Array,R="NEEDLE_progressive",w=se("debugprogressive"),Q=Symbol("needle-progressive-texture"),W=new Map,j=new Set;if(w){let l=function(){e+=1,console.log("Toggle LOD level",e,W),W.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n.isBufferGeometry===!0){const a=S.getMeshLODInformation(n),d=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,d),a&&(t=Math.max(t,a.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,S.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,j&&j.forEach(o=>{o.name!="BackgroundCubeMaterial"&&"wireframe"in o&&(o.wireframe=r)}))})}function ie(l,e,t){var i;if(!w)return;W.has(l)||W.set(l,{keys:[],sourceId:t});const r=W.get(l);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const v=class{constructor(e,t){c(this,"parser");c(this,"url");c(this,"_isLoadingMesh");c(this,"loadMesh",e=>{var r,i;if(this._isLoadingMesh)return null;const t=(i=(r=this.parser.json.meshes[e])==null?void 0:r.extensions)==null?void 0:i[R];return t?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",e).then(o=>(this._isLoadingMesh=!1,o&&v.registerMesh(this.url,t.guid,o,t.lods.length,void 0,t),o))):null});w&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return R}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static getMaterialMinMaxLODsCount(e,t){const r="LODS:minmax",i=e[r];if(i!=null)return i;if(t||(t={min_count:1/0,max_count:0,lods:[]}),Array.isArray(e)){for(const s of e)this.getMaterialMinMaxLODsCount(s,t);return e[r]=t,t}w==="verbose"&&console.log("getMaterialMinMaxLODsCount",e);const o=s=>{const n=this.getAssignedLODInformation(s);if(n){const a=this.lodInfos.get(n.key);if(a&&a.lods){t.min_count=Math.min(t.min_count,a.lods.length),t.max_count=Math.max(t.max_count,a.lods.length);for(let d=0;d<a.lods.length;d++){const h=a.lods[d];h.width&&(t.lods[d]=t.lods[d]||{min_height:1/0,max_height:0},t.lods[d].min_height=Math.min(t.lods[d].min_height,h.height),t.lods[d].max_height=Math.max(t.lods[d].max_height,h.height))}}}};if(e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const s=e;for(const n of Object.keys(s.uniforms)){const a=s.uniforms[n].value;(a==null?void 0:a.isTexture)===!0&&o(a)}}else if(e.isMaterial)for(const s of Object.keys(e)){const n=e[s];(n==null?void 0:n.isTexture)===!0&&o(n)}return e[r]=t,t}static hasLODLevelAvailable(e,t){var o;if(Array.isArray(e)){for(const s of e)if(this.hasLODLevelAvailable(s,t))return!0;return!1}if(e.isMaterial===!0){for(const s of Object.keys(e)){const n=e[s];if(n&&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 g.Mesh||e.isMesh===!0){const i=e.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of I)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,v.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 g.BufferGeometry&&(e.geometry=s,w&&ie(e,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else w&&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 g.Material||e.isMaterial===!0){const r=e,i=[],o=new Array;if(w&&j.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const n of Object.keys(s.uniforms)){const a=s.uniforms[n].value;if((a==null?void 0:a.isTexture)===!0){const d=this.assignTextureLODForSlot(a,t,r,n);i.push(d),o.push(n)}}}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],h=o[a];d&&d.isTexture===!0?n.push({material:r,slot:h,texture:d,level:t}):n.push({material:r,slot:h,texture:null,level:t})}return n})}if(e instanceof g.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):i==="glyphMap"?Promise.resolve(e):v.getOrLoadLOD(e,t).then(o=>{if(Array.isArray(o))return null;if((o==null?void 0:o.isTexture)===!0){if(o!=e){if(r&&i){const s=r[i];if(s){const n=this.getAssignedLODInformation(s);if(n&&(n==null?void 0:n.level)<t)return w==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,t,r,s,o),null}r[i]=o}if(w&&i&&r){const s=this.getAssignedLODInformation(e);s&&ie(r,i,s.url)}}return o}else w=="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 w&&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[R];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,v.registerTexture(this.url,a,s.lods.length,o,s));n||this.parser.getDependency("texture",o).then(a=>{a&&v.registerTexture(this.url,a,s.lods.length,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[R];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&&v.registerMesh(this.url,s.guid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,d,h;const r=w=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const D=e;D.source&&D.source[Q]&&(s=D.source[Q])}if(s||(s=v.lodInfos.get(o)),s){if(t>0){let L=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?L=!0:M||(L=!0),L)return this.lowresCache.get(o)}const D=Array.isArray(s.lods)?(n=s.lods[t])==null?void 0:n.path:s.lods;if(!D)return w&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const f=De(i.url,D);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const L=f+"_"+s.guid,M=this.previouslyLoaded.get(L);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${L}`);let x=await M.catch(F=>(console.error(`Error loading LOD ${t} from ${f}
2
+ `,F),null)),A=!1;if(x==null||(x instanceof g.Texture&&e instanceof g.Texture?(a=x.image)!=null&&a.data||(d=x.source)!=null&&d.data?x=this.copySettings(e,x):(A=!0,this.previouslyLoaded.delete(L)):x instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((h=x.attributes.position)!=null&&h.array||(A=!0,this.previouslyLoaded.delete(L)))),!A)return x}const O=s,_=new Promise(async(x,A)=>{const F=new Le.GLTFLoader;re(F),w&&(await new Promise(p=>setTimeout(p,1e3)),r&&console.warn("Start loading (delayed) "+f,O.guid));let Y=f;if(O&&Array.isArray(O.lods)){const p=O.lods[t];p.hash&&(Y+="?v="+p.hash)}const B=await F.loadAsync(Y).catch(p=>(console.error(`Error loading LOD ${t} from ${f}
3
+ `,p),null));if(!B)return null;const H=B.parser;r&&console.log("Loading finished "+f,O.guid);let y=0;if(B.parser.json.textures){let p=!1;for(const u of B.parser.json.textures){if(u!=null&&u.extensions){const m=u==null?void 0:u.extensions[R];if(m!=null&&m.guid&&m.guid===O.guid){p=!0;break}}y++}if(p){let u=await H.getDependency("texture",y);return u&&v.assignLODInformation(i.url,u,o,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+u.name+'"',f,y,u,L),e instanceof g.Texture&&(u=this.copySettings(e,u)),u&&(u.guid=O.guid),x(u)}else w&&console.warn("Could not find texture with guid",O.guid)}if(y=0,B.parser.json.meshes){let p=!1;for(const u of B.parser.json.meshes){if(u!=null&&u.extensions){const m=u==null?void 0:u.extensions[R];if(m!=null&&m.guid&&m.guid===O.guid){p=!0;break}}y++}if(p){const u=await H.getDependency("mesh",y),m=O;if(r&&console.log(`Loaded Mesh "${u.name}"`,f,y,u,L),u.isMesh===!0){const T=u.geometry;return v.assignLODInformation(i.url,T,o,t,void 0,m.density),x(T)}else{const T=new Array;for(let C=0;C<u.children.length;C++){const G=u.children[C];if(G instanceof g.Mesh){const N=G.geometry;v.assignLODInformation(i.url,N,o,t,C,m.density),T.push(N)}}return x(T)}}}return x(null)});return this.previouslyLoaded.set(L,_),await _}else if(e instanceof g.Texture){r&&console.log("Load texture from uri: "+f);const M=await new g.TextureLoader().loadAsync(f);return M?(M.guid=s.guid,M.flipY=!1,M.needsUpdate=!0,M.colorSpace=e.colorSpace,r&&console.log(s,M)):w&&console.warn("failed loading",f),M}}else w&&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 Me(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){return t=t.clone(),w&&console.warn(`Copying texture settings
4
4
  `,e.uuid,`
5
- `,t.uuid),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.mipmaps||(t.generateMipmaps=e.generateMipmaps),t}};let S=v;u(S,"registerTexture",(e,t,r,i)=>{_&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[J]=i);const n=i.guid;v.assignLODInformation(e,t,n,0,0,void 0),v.lodInfos.set(n,i),v.lowresCache.set(n,t)}),u(S,"registerMesh",(e,t,r,i,n,s)=>{var d;_&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),v.assignLODInformation(e,o,t,i,n,s.density),v.lodInfos.set(t,s);let l=v.lowresCache.get(t);l?l.push(r.geometry):l=[r.geometry],v.lowresCache.set(t,l),i>0&&!ae(r)&&le(r,o);for(const h of R)(d=h.onRegisteredNewMesh)==null||d.call(h,r,s)}),u(S,"lodInfos",new Map),u(S,"previouslyLoaded",new Map),u(S,"lowresCache",new Map);class Me{constructor(e,t,r,i,n){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),n!=null&&(this.density=n)}}const Q=se("debugprogressive"),Oe=se("noprogressive"),ne=Symbol("Needle:LODSManager"),P={mesh_lod:-1,texture_lod:-1},A=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new g.Matrix4);u(this,"cameraFrustrum",new g.Frustum);u(this,"targetTriangleDensity",2e5);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"_frame",0);u(this,"_originalRender");u(this,"_sphere",new g.Sphere);u(this,"_tempBox",new g.Box3);u(this,"_tempBox2",new g.Box3);u(this,"tempMatrix",new g.Matrix4);u(this,"_tempWorldPosition",new g.Vector3);u(this,"_tempBoxSize",new g.Vector3);u(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){R.push(e)}static removePlugin(e){const t=R.indexOf(e);t>=0&&R.splice(t,1)}static get(e){return e[ne]?e[ne]:new A(e)}get plugins(){return R}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,i){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const s=t._frame,o=e++;t.onBeforeRender(r,i,o,s),t._originalRender.call(this,r,i),t.onAfterRender(r,i,o,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var l,d;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),s=n.opaque;let o=!0;if(s.length===1){const h=s[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(o=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(o=!1),o){if(Oe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of s){if(f.material&&(((l=f.geometry)==null?void 0:l.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){Q&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}const L=n.transparent;for(const f of L){const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}}}updateLODs(e,t,r,i,n){var l,d;let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const h of R)(l=h.onBeforeUpdateLOD)==null||l.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,s,i,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod);let o=P.texture_lod;if(r.material&&o>=0){const h=r["DEBUG:LOD"];h!=null&&(o=h),this.loadProgressiveTextures(r.material,o)}for(const h of R)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lasLodLevel_Texture=P.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const n of e)this.loadProgressiveTextures(n,t);return}const r=e;let i=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(i=!0),i&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}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 S.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,i=e.max,n=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,i,n){var D;if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}let o=10+1;if(Q&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const l=S.getMeshLODInformation(t.geometry),d=l==null?void 0:l.lods,h=d&&d.length>0,L=S.getMaterialMinMaxLODsCount(t.material),f=(L==null?void 0:L.min)!=1/0&&L.min>0&&L.max>0;if(!h&&!f){n.mesh_lod=0,n.texture_lod=0;return}if(h||(o=0),!((D=this.cameraFrustrum)!=null&&D.intersectsObject(t))){n.mesh_lod=99,n.texture_lod=99;return}const m=t.geometry.boundingBox;if(m&&e.isPerspectiveCamera){const O=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 y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(m),this._tempBox.applyMatrix4(t.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let c=y.x,x=y.y,T=p.x,C=p.y;const I=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;c=(c-V)*I+V,x=(x-q)*I+q,T=(T-V)*I+V,C=(C-q)*I+q;const ue=c<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),fe=x<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),H=Math.max(ue,fe);r.lastCentrality=(N-H)*(N-H)*(N-H)}else r.lastCentrality=1;const M=this._tempBox.getSize(this._tempBoxSize);M.multiplyScalar(.5),screen.availHeight>0&&M.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),M.x*=O.aspect;const G=e.matrixWorldInverse,w=this._tempBox2;w.copy(m),w.applyMatrix4(t.matrixWorld),w.applyMatrix4(G);const E=w.getSize(this._tempBox2Size),F=Math.max(E.x,E.y);if(Math.max(M.x,M.y)!=0&&F!=0&&(M.z=E.z/Math.max(E.x,E.y)*Math.max(M.x,M.y)),r.lastScreenCoverage=Math.max(M.x,M.y,M.z),r.lastScreenspaceVolume.copy(M),r.lastScreenCoverage*=r.lastCentrality,Q&&A.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=A.corner0,c=A.corner1,x=A.corner2,T=A.corner3;p.copy(this._tempBox.min),c.copy(this._tempBox.max),c.x=p.x,x.copy(this._tempBox.max),x.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=c.z=x.z=T.z=C,p.applyMatrix4(y),c.applyMatrix4(y),x.applyMatrix4(y),T.applyMatrix4(y),A.debugDrawLine(p,c,255),A.debugDrawLine(p,x,255),A.debugDrawLine(c,T,255),A.debugDrawLine(x,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<i){B=y;break}}B<o&&(o=B)}if(n.mesh_lod=o,L!=null&&L.min&&L.min>0&&L.max>0){const O=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=we(L.max,0,O)}else n.texture_lod=0}};let b=A;u(b,"debugDrawLine"),u(b,"corner0",new g.Vector3),u(b,"corner1",new g.Vector3),u(b,"corner2",new g.Vector3),u(b,"corner3",new g.Vector3),u(b,"_tempPtInside",new g.Vector3);function we(a,e,t){return a+(e-a)*t}class _e{constructor(){u(this,"frames",0);u(this,"lastLodLevel_Mesh",0);u(this,"lasLodLevel_Texture",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new g.Vector3);u(this,"lastCentrality",0)}}const oe=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(a){if(!a)return null;let e=null,t=null;for(let r=a;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),n=i.find(o=>o.toString()=="Symbol(renderer)"),s=i.find(o=>o.toString()=="Symbol(scene)");!e&&n!=null&&(e=a[n].threeRenderer),!t&&s!=null&&(t=a[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=b.get(e);if(b.addPlugin(new ve(a)),r.enable(),t){const i=t.camera||t.traverse(n=>n.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class ve{constructor(e){u(this,"modelviewer");u(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[X]==!0)return;t[X]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let n=function(o){var d,h,L;if(o[X]==!0)return;o[X]=!0,o.userData&&(o.userData.LOD=-1);const l=Object.keys(o);for(let f=0;f<l.length;f++){const m=l[f],D=o[m];if((D==null?void 0:D.isTexture)===!0){const O=(h=(d=D.userData)==null?void 0:d.associations)==null?void 0:h.textures,M=r.parser.json.textures[O];if(!M){console.warn("Texture data not found for texture index "+O);continue}if((L=M==null?void 0:M.extensions)!=null&&L[k]){const G=M.extensions[k];G&&i&&S.registerTexture(i,D,G.lods.length,G)}}}};const s=t.material;if(Array.isArray(s))for(const o of s)n(o);else n(s)}}tryParseMeshLOD(e,t){var n,s;if(t[oe]==!0)return;t[oe]=!0;const r=this.getUrl();if(!r)return;const i=(s=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[k];if(i&&r){const o=t.uuid;S.registerMesh(r,o,t,0,i.lods.length,i)}}}function Se(a,e,t,r){te(e),re(t),t.register(n=>new S(n,a));const i=b.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=k;exports.LODsManager=b;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=Le;exports.setKTX2TranscoderLocation=xe;exports.setRaycastMesh=le;exports.useNeedleProgressive=Se;
5
+ `,t.uuid),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.mipmaps||(t.generateMipmaps=e.generateMipmaps),t}};let S=v;c(S,"registerTexture",(e,t,r,i,o)=>{w&&console.log("> Progressive: register texture",i,t.name,t.uuid,t,o),t.source&&(t.source[Q]=o);const s=o.guid;v.assignLODInformation(e,t,s,r,i,void 0),v.lodInfos.set(s,o),v.lowresCache.set(s,t)}),c(S,"registerMesh",(e,t,r,i,o,s)=>{var d;w&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;n.userData||(n.userData={}),v.assignLODInformation(e,n,t,i,o,s.density),v.lodInfos.set(t,s);let a=v.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],v.lowresCache.set(t,a),i>0&&!ae(r)&&le(r,n);for(const h of I)(d=h.onRegisteredNewMesh)==null||d.call(h,r,s)}),c(S,"lodInfos",new Map),c(S,"previouslyLoaded",new Map),c(S,"lowresCache",new Map);class Me{constructor(e,t,r,i,o){c(this,"url");c(this,"key");c(this,"level");c(this,"index");c(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const z=se("debugprogressive"),Oe=se("noprogressive"),oe=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},b=class{constructor(e){c(this,"renderer");c(this,"projectionScreenMatrix",new g.Matrix4);c(this,"cameraFrustrum",new g.Frustum);c(this,"targetTriangleDensity",2e5);c(this,"updateInterval",0);c(this,"pause",!1);c(this,"_frame",0);c(this,"_originalRender");c(this,"_sphere",new g.Sphere);c(this,"_tempBox",new g.Box3);c(this,"_tempBox2",new g.Box3);c(this,"tempMatrix",new g.Matrix4);c(this,"_tempWorldPosition",new g.Vector3);c(this,"_tempBoxSize",new g.Vector3);c(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){I.push(e)}static removePlugin(e){const t=I.indexOf(e);t>=0&&I.splice(t,1)}static get(e){return e[oe]?e[oe]:new b(e)}get plugins(){return I}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,i){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const s=t._frame,n=e++;t.onBeforeRender(r,i,n,s),t._originalRender.call(this,r,i),t.onAfterRender(r,i,n,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var a,d;if(this.pause)return;const o=this.renderer.renderLists.get(e,0),s=o.opaque;let n=!0;if(s.length===1){const h=s[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(n=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(n=!1),n){if(Oe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of s){if(f.material&&(((a=f.geometry)==null?void 0:a.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){z&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const L=f.object;(L instanceof g.Mesh||L.isMesh)&&this.updateLODs(e,t,L,h,i)}const D=o.transparent;for(const f of D){const L=f.object;(L instanceof g.Mesh||L.isMesh)&&this.updateLODs(e,t,L,h,i)}}}updateLODs(e,t,r,i,o){var a,d;let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const h of I)(a=h.onBeforeUpdateLOD)==null||a.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,s,i,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let n=E.texture_lod;if(r.material&&n>=0){const h=r["DEBUG:LOD"];h!=null&&(n=h),this.loadProgressiveTextures(r.material,n)}for(const h of I)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,E);s.lastLodLevel_Mesh=E.mesh_lod,s.lastLodLevel_Texture=E.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const o of e)this.loadProgressiveTextures(o,t);return}const r=e;let i=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(i=!0),i&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}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 S.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,i=e.max,o=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(o,s,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,i,o){var M;if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}let n=10+1;if(z&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const a=S.getMeshLODInformation(t.geometry),d=a==null?void 0:a.lods,h=d&&d.length>0,D=S.getMaterialMinMaxLODsCount(t.material),f=(D==null?void 0:D.min_count)!=1/0&&D.min_count>0&&D.max_count>0;if(!h&&!f){o.mesh_lod=0,o.texture_lod=0;return}if(h||(n=0),!((M=this.cameraFrustrum)!=null&&M.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const L=t.geometry.boundingBox;if(L&&e.isPerspectiveCamera){const O=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 y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(L),this._tempBox.applyMatrix4(t.matrixWorld),b.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let u=y.x,m=y.y,T=p.x,C=p.y;const G=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;u=(u-V)*G+V,m=(m-q)*G+q,T=(T-V)*G+V,C=(C-q)*G+q;const ue=u<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),de=m<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),J=Math.max(ue,de);r.lastCentrality=(N-J)*(N-J)*(N-J)}else r.lastCentrality=1;const _=this._tempBox.getSize(this._tempBoxSize);_.multiplyScalar(.5),screen.availHeight>0&&_.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),_.x*=O.aspect;const k=e.matrixWorldInverse,x=this._tempBox2;x.copy(L),x.applyMatrix4(t.matrixWorld),x.applyMatrix4(k);const A=x.getSize(this._tempBox2Size),F=Math.max(A.x,A.y);if(Math.max(_.x,_.y)!=0&&F!=0&&(_.z=A.z/Math.max(A.x,A.y)*Math.max(_.x,_.y)),r.lastScreenCoverage=Math.max(_.x,_.y,_.z),r.lastScreenspaceVolume.copy(_),r.lastScreenCoverage*=r.lastCentrality,z&&b.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=b.corner0,u=b.corner1,m=b.corner2,T=b.corner3;p.copy(this._tempBox.min),u.copy(this._tempBox.max),u.x=p.x,m.copy(this._tempBox.max),m.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=u.z=m.z=T.z=C,p.applyMatrix4(y),u.applyMatrix4(y),m.applyMatrix4(y),T.applyMatrix4(y),b.debugDrawLine(p,u,255),b.debugDrawLine(p,m,255),b.debugDrawLine(u,T,255),b.debugDrawLine(m,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<i){B=y;break}}B<n&&(n=B)}if(o.mesh_lod=n,f)if(r.lastLodLevel_Texture<0){if(o.texture_lod=D.max_count-1,z){const O=D.lods[D.max_count-1];z&&console.log(`First Texture LOD ${o.texture_lod} (${O.max_height}px) - ${t.name}`)}}else{const O=r.lastScreenCoverage*1.5,k=this.renderer.domElement.clientHeight/window.devicePixelRatio*O;for(let x=D.lods.length-1;x>=0;x--){const A=D.lods[x];if(A.max_height>k){o.texture_lod=x,o.texture_lod<r.lastLodLevel_Texture&&z&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} → ${o.texture_lod} (${A.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${k.toFixed(0)}px) - ${t.name}`);break}}}else o.texture_lod=0}};let P=b;c(P,"debugDrawLine"),c(P,"corner0",new g.Vector3),c(P,"corner1",new g.Vector3),c(P,"corner2",new g.Vector3),c(P,"corner3",new g.Vector3),c(P,"_tempPtInside",new g.Vector3);class _e{constructor(){c(this,"frames",0);c(this,"lastLodLevel_Mesh",-1);c(this,"lastLodLevel_Texture",-1);c(this,"lastScreenCoverage",0);c(this,"lastScreenspaceVolume",new g.Vector3);c(this,"lastCentrality",0)}}const ne=Symbol("NEEDLE_mesh_lod"),K=Symbol("NEEDLE_texture_lod");function ce(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=P.get(e);if(P.addPlugin(new we(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 we{constructor(e){c(this,"modelviewer");c(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[K]==!0)return;t[K]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let o=function(n){var d,h,D;if(n[K]==!0)return;n[K]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let f=0;f<a.length;f++){const L=a[f],M=n[L];if((M==null?void 0:M.isTexture)===!0){const O=(h=(d=M.userData)==null?void 0:d.associations)==null?void 0:h.textures,_=r.parser.json.textures[O];if(!_){console.warn("Texture data not found for texture index "+O);continue}if((D=_==null?void 0:_.extensions)!=null&&D[R]){const k=_.extensions[R];k&&i&&S.registerTexture(i,M,k.lods.length,O,k)}}}};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[ne]==!0)return;t[ne]=!0;const r=this.getUrl();if(!r)return;const i=(s=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:s[R];if(i&&r){const n=t.uuid;S.registerMesh(r,n,t,0,i.lods.length,i)}}}function ve(l,e,t,r){te(e),re(t),t.register(o=>new S(o,l));const i=P.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=R;exports.LODsManager=P;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=xe;exports.setKTX2TranscoderLocation=me;exports.setRaycastMesh=le;exports.useNeedleProgressive=ve;
@@ -10,7 +10,12 @@ declare type NEEDLE_progressive_model = {
10
10
  guid: string;
11
11
  lods: Array<NEEDLE_progressive_model_LOD>;
12
12
  };
13
- export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {};
13
+ export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {
14
+ lods: Array<NEEDLE_progressive_model_LOD & {
15
+ width: number;
16
+ height: number;
17
+ }>;
18
+ };
14
19
  export declare type NEEDLE_progressive_mesh_model = NEEDLE_progressive_model & {
15
20
  density: number;
16
21
  lods: Array<NEEDLE_progressive_model_LOD & {
@@ -33,6 +38,14 @@ export declare type ProgressiveMaterialTextureLoadingResult = {
33
38
  /** the level of detail that was loaded */
34
39
  level: number;
35
40
  };
41
+ declare type TextureLODsMinMaxInfo = {
42
+ min_count: number;
43
+ max_count: number;
44
+ lods: Array<{
45
+ min_height: number;
46
+ max_height: number;
47
+ }>;
48
+ };
36
49
  /**
37
50
  * The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
38
51
  * This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
@@ -52,13 +65,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
52
65
  /** The name of the extension */
53
66
  get name(): string;
54
67
  static getMeshLODInformation(geo: BufferGeometry): NEEDLE_progressive_mesh_model | null;
55
- static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?: {
56
- min: number;
57
- max: number;
58
- }): {
59
- min: number;
60
- max: number;
61
- };
68
+ static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?: TextureLODsMinMaxInfo): TextureLODsMinMaxInfo;
62
69
  /** Check if a LOD level is available for a mesh or a texture
63
70
  * @param obj the mesh or texture to check
64
71
  * @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
@@ -92,11 +99,13 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
92
99
  private readonly parser;
93
100
  private readonly url;
94
101
  constructor(parser: GLTFParser, url: string);
102
+ private _isLoadingMesh;
103
+ loadMesh: (meshIndex: number) => Promise<any> | null;
95
104
  afterRoot(gltf: GLTF): null;
96
105
  /**
97
106
  * Register a texture with LOD information
98
107
  */
99
- static registerTexture: (url: string, tex: Texture, index: number, ext: NEEDLE_progressive_texture_model) => void;
108
+ static registerTexture: (url: string, tex: Texture, level: number, index: number, ext: NEEDLE_progressive_texture_model) => void;
100
109
  /**
101
110
  * Register a mesh with LOD information
102
111
  */
package/lib/extension.js CHANGED
@@ -95,28 +95,52 @@ export class NEEDLE_progressive {
95
95
  }
96
96
  return null;
97
97
  }
98
- static getMaterialMinMaxLODsCount(material, minmax = { min: Infinity, max: -1 }) {
98
+ static getMaterialMinMaxLODsCount(material, minmax) {
99
+ // we can cache this material min max data because it wont change at runtime
100
+ const cacheKey = "LODS:minmax";
101
+ const cached = material[cacheKey];
102
+ if (cached != undefined)
103
+ return cached;
104
+ if (!minmax) {
105
+ minmax = {
106
+ min_count: Infinity,
107
+ max_count: 0,
108
+ lods: [],
109
+ };
110
+ }
99
111
  if (Array.isArray(material)) {
100
112
  for (const mat of material) {
101
113
  this.getMaterialMinMaxLODsCount(mat, minmax);
102
114
  }
115
+ material[cacheKey] = minmax;
103
116
  return minmax;
104
117
  }
105
- let min = minmax.min;
106
- let max = minmax.max;
118
+ if (debug === "verbose")
119
+ console.log("getMaterialMinMaxLODsCount", material);
120
+ const processTexture = (tex) => {
121
+ const info = this.getAssignedLODInformation(tex);
122
+ if (info) {
123
+ const model = this.lodInfos.get(info.key);
124
+ if (model && model.lods) {
125
+ minmax.min_count = Math.min(minmax.min_count, model.lods.length);
126
+ minmax.max_count = Math.max(minmax.max_count, model.lods.length);
127
+ for (let i = 0; i < model.lods.length; i++) {
128
+ const lod = model.lods[i];
129
+ if (lod.width) {
130
+ minmax.lods[i] = minmax.lods[i] || { min_height: Infinity, max_height: 0 };
131
+ minmax.lods[i].min_height = Math.min(minmax.lods[i].min_height, lod.height);
132
+ minmax.lods[i].max_height = Math.max(minmax.lods[i].max_height, lod.height);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ };
107
138
  if (material.type === "ShaderMaterial" || material.type === "RawShaderMaterial") {
108
139
  const mat = material;
109
140
  for (const slot of Object.keys(mat.uniforms)) {
110
141
  const val = mat.uniforms[slot].value;
111
142
  if (val?.isTexture === true) {
112
- const info = this.getAssignedLODInformation(val);
113
- if (info) {
114
- const model = this.lodInfos.get(info.key);
115
- if (model && model.lods) {
116
- min = Math.min(min, model.lods.length);
117
- max = Math.max(max, model.lods.length);
118
- }
119
- }
143
+ processTexture(val);
120
144
  }
121
145
  }
122
146
  }
@@ -124,19 +148,11 @@ export class NEEDLE_progressive {
124
148
  for (const slot of Object.keys(material)) {
125
149
  const val = material[slot];
126
150
  if (val?.isTexture === true) {
127
- const info = this.getAssignedLODInformation(val);
128
- if (info) {
129
- const model = this.lodInfos.get(info.key);
130
- if (model && model.lods) {
131
- min = Math.min(min, model.lods.length);
132
- max = Math.max(max, model.lods.length);
133
- }
134
- }
151
+ processTexture(val);
135
152
  }
136
153
  }
137
154
  }
138
- minmax.min = min;
139
- minmax.max = max;
155
+ material[cacheKey] = minmax;
140
156
  return minmax;
141
157
  }
142
158
  /** Check if a LOD level is available for a mesh or a texture
@@ -317,29 +333,29 @@ export class NEEDLE_progressive {
317
333
  static assignTextureLODForSlot(current, level, material, slot) {
318
334
  if (current?.isTexture !== true)
319
335
  return Promise.resolve(null);
320
- // if (debug) console.log("-----------\n", "FIND", material?.name, slot, current?.name, current?.userData, current, material);
321
336
  if (slot === "glyphMap") {
322
337
  return Promise.resolve(current);
323
338
  }
324
- // save which texture level was requested last
325
- const key = "LOD:requested level:" + slot;
326
- if (material)
327
- material[key] = level;
328
339
  return NEEDLE_progressive.getOrLoadLOD(current, level).then(tex => {
329
- // check if the requested level has changed in the meantime
330
- if (material && material[key] != level)
331
- return null;
332
- // if (material) delete material[key];
333
340
  // this can currently not happen
334
341
  if (Array.isArray(tex))
335
342
  return null;
336
343
  if (tex?.isTexture === true) {
337
344
  if (tex != current) {
338
- // if (debug) console.warn("Assign LOD", material?.name, slot, tex.name, tex["guid"], material, "Prev:", current, "Now:", tex, "\n--------------");
339
- // tex.needsUpdate = true;
340
345
  if (material && slot) {
346
+ const assigned = material[slot];
347
+ // Check if the assigned texture LOD is higher quality than the current LOD
348
+ // This is necessary for cases where e.g. a texture is updated via an explicit call to assignTextureLOD
349
+ if (assigned) {
350
+ const assignedLOD = this.getAssignedLODInformation(assigned);
351
+ if (assignedLOD && assignedLOD?.level < level) {
352
+ if (debug === "verbose")
353
+ console.warn("Assigned texture level is already higher: ", assignedLOD.level, level, material, assigned, tex);
354
+ return null;
355
+ }
356
+ // assigned.dispose();
357
+ }
341
358
  material[slot] = tex;
342
- // material.needsUpdate = true;
343
359
  }
344
360
  if (debug && slot && material) {
345
361
  const lodinfo = this.getAssignedLODInformation(current);
@@ -377,6 +393,22 @@ export class NEEDLE_progressive {
377
393
  this.parser = parser;
378
394
  this.url = url;
379
395
  }
396
+ _isLoadingMesh;
397
+ loadMesh = (meshIndex) => {
398
+ if (this._isLoadingMesh)
399
+ return null;
400
+ const ext = this.parser.json.meshes[meshIndex]?.extensions?.[EXTENSION_NAME];
401
+ if (!ext)
402
+ return null;
403
+ this._isLoadingMesh = true;
404
+ return this.parser.getDependency("mesh", meshIndex).then(mesh => {
405
+ this._isLoadingMesh = false;
406
+ if (mesh) {
407
+ NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh, ext.lods.length, undefined, ext);
408
+ }
409
+ return mesh;
410
+ });
411
+ };
380
412
  afterRoot(gltf) {
381
413
  if (debug)
382
414
  console.log("AFTER", this.url, gltf);
@@ -385,21 +417,20 @@ export class NEEDLE_progressive {
385
417
  const ext = textureInfo?.extensions[EXTENSION_NAME];
386
418
  if (ext) {
387
419
  let found = false;
388
- // TODO: why are sometimes textures not in the associations array when using r3f glTF loader...
389
420
  for (const key of this.parser.associations.keys()) {
390
421
  if (key.isTexture === true) {
391
422
  const val = this.parser.associations.get(key);
392
423
  if (val.textures === index) {
393
424
  found = true;
394
- NEEDLE_progressive.registerTexture(this.url, key, index, ext);
425
+ NEEDLE_progressive.registerTexture(this.url, key, ext.lods.length, index, ext);
395
426
  }
396
427
  }
397
428
  }
429
+ // If textures aren't used there are no associations - we still want to register the LOD info so we create one instance
398
430
  if (!found) {
399
- // see TODO above, fallback for when textures are not in the associations array
400
431
  this.parser.getDependency("texture", index).then(tex => {
401
432
  if (tex) {
402
- NEEDLE_progressive.registerTexture(this.url, tex, index, ext);
433
+ NEEDLE_progressive.registerTexture(this.url, tex, ext.lods.length, index, ext);
403
434
  }
404
435
  });
405
436
  }
@@ -410,14 +441,25 @@ export class NEEDLE_progressive {
410
441
  if (meshInfo?.extensions) {
411
442
  const ext = meshInfo?.extensions[EXTENSION_NAME];
412
443
  if (ext && ext.lods) {
444
+ let found = false;
413
445
  for (const entry of this.parser.associations.keys()) {
414
446
  if (entry.isMesh) {
415
447
  const val = this.parser.associations.get(entry);
416
448
  if (val.meshes === index) {
449
+ found = true;
417
450
  NEEDLE_progressive.registerMesh(this.url, ext.guid, entry, ext.lods.length, val.primitives, ext);
418
451
  }
419
452
  }
420
453
  }
454
+ // Note: we use loadMesh rather than this method so the mesh is surely registered at the right time when the mesh is created
455
+ // // If meshes aren't used there are no associations - we still want to register the LOD info so we create one instance
456
+ // if (!found) {
457
+ // this.parser.getDependency("mesh", index).then(mesh => {
458
+ // if (mesh) {
459
+ // NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh as Mesh, ext.lods.length, undefined, ext);
460
+ // }
461
+ // });
462
+ // }
421
463
  }
422
464
  }
423
465
  });
@@ -426,7 +468,7 @@ export class NEEDLE_progressive {
426
468
  /**
427
469
  * Register a texture with LOD information
428
470
  */
429
- static registerTexture = (url, tex, index, ext) => {
471
+ static registerTexture = (url, tex, level, index, ext) => {
430
472
  if (debug)
431
473
  console.log("> Progressive: register texture", index, tex.name, tex.uuid, tex, ext);
432
474
  // Put the extension info into the source (seems like tiled textures are cloned and the userdata etc is not properly copied BUT the source of course is not cloned)
@@ -434,7 +476,7 @@ export class NEEDLE_progressive {
434
476
  if (tex.source)
435
477
  tex.source[$progressiveTextureExtension] = ext;
436
478
  const LODKEY = ext.guid;
437
- NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, 0, 0, undefined);
479
+ NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, level, index, undefined);
438
480
  NEEDLE_progressive.lodInfos.set(LODKEY, ext);
439
481
  NEEDLE_progressive.lowresCache.set(LODKEY, tex);
440
482
  };
@@ -108,7 +108,7 @@ export declare class LODsManager {
108
108
  declare class LOD_state {
109
109
  frames: number;
110
110
  lastLodLevel_Mesh: number;
111
- lasLodLevel_Texture: number;
111
+ lastLodLevel_Texture: number;
112
112
  lastScreenCoverage: number;
113
113
  readonly lastScreenspaceVolume: Vector3;
114
114
  lastCentrality: number;