@needle-tools/gltf-progressive 1.2.0-alpha.2 → 1.2.0-alpha.4

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,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 a of o.keys){const i=s[a];if(i.isBufferGeometry===!0){const u=_.getMeshLODInformation(i),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(a=>(this._isLoadingMesh=!1,a&&M.registerMesh(this.url,s.guid,a,s.lods.length,void 0,s),a))):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=this,n="LODS:minmax",o=t[n];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[n]=e,e}if(L==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const i of Object.keys(a.uniforms)){const u=a.uniforms[i].value;u?.isTexture===!0&&s(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const i=t[a];i?.isTexture===!0&&s(i,e)}return t[n]=e,e;function s(a,i){const u=r.getAssignedLODInformation(a);if(u){const l=r.lodInfos.get(u.key);if(l&&l.lods){i.min_count=Math.min(i.min_count,l.lods.length),i.max_count=Math.max(i.max_count,l.lods.length);for(let g=0;g<l.lods.length;g++){const d=l.lods[g];d.width&&(i.lods[g]=i.lods[g]||{min_height:1/0,max_height:0},i.lods[g].min_height=Math.min(i.lods[g].min_height,d.height),i.lods[g].max_height=Math.max(i.lods[g].max_height,d.height))}}}}}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 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 a=o.index||0;s=s[a]}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 a of Object.keys(s.uniforms)){const i=s.uniforms[a].value;if(i?.isTexture===!0){const u=this.assignTextureLODForSlot(i,e,r,a);n.push(u),o.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);n.push(i),o.push(s)}}return Promise.all(n).then(s=>{const a=new Array;for(let i=0;i<s.length;i++){const u=s[i],l=o[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 $||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 a=this.getAssignedLODInformation(s);if(a&&a?.level<e)return L==="verbose"&&console.warn("Assigned texture level is already higher: ",a.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 a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===o&&(a=!0,M.registerTexture(this.url,i,s.lods.length,o,s));a||this.parser.getDependency("texture",o).then(i=>{i&&M.registerTexture(this.url,i,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 a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===o&&M.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var r,n,o,s;const a=L=="verbose",i=t.userData.LODS;if(!i)return null;const u=i?.key;let l;if(t.isTexture===!0){const g=t;g.source&&g.source[te]&&(l=g.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 g=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!g)return L&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const d=we(i.url,g);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=d+"_"+l.guid,w=this.previouslyLoaded.get(f);if(w!==void 0){a&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let p=await w.catch(G=>(console.error(`Error loading LOD ${e} from ${d}
2
- `,G),null)),b=!1;if(p==null||(p instanceof $&&t instanceof $?(n=p.image)!=null&&n.data||(o=p.source)!=null&&o.data?p=this.copySettings(t,p):(b=!0,this.previouslyLoaded.delete(f)):p instanceof q&&t instanceof q&&((s=p.attributes.position)!=null&&s.array||(b=!0,this.previouslyLoaded.delete(f)))),!b)return p}const m=l,S=new Promise(async(p,b)=>{const G=new Me;Z(G),L&&(await new Promise(y=>setTimeout(y,1e3)),a&&console.warn("Start loading (delayed) "+d,m.guid));let N=d;if(m&&Array.isArray(m.lods)){const y=m.lods[e];y.hash&&(N+="?v="+y.hash)}const x=await G.loadAsync(N).catch(y=>(console.error(`Error loading LOD ${e} from ${d}
3
- `,y),null));if(!x)return null;const v=x.parser;a&&console.log("Loading finished "+d,m.guid);let D=0;if(x.parser.json.textures){let y=!1;for(const h of x.parser.json.textures){if(h!=null&&h.extensions){const O=h?.extensions[P];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){let h=await v.getDependency("texture",D);return h&&M.assignLODInformation(i.url,h,u,e,void 0,void 0),a&&console.log('change "'+t.name+'" \u2192 "'+h.name+'"',d,D,h,f),t instanceof $&&(h=this.copySettings(t,h)),h&&(h.guid=m.guid),p(h)}else L&&console.warn("Could not find texture with guid",m.guid)}if(D=0,x.parser.json.meshes){let y=!1;for(const h of x.parser.json.meshes){if(h!=null&&h.extensions){const O=h?.extensions[P];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){const h=await v.getDependency("mesh",D),O=m;if(a&&console.log(`Loaded Mesh "${h.name}"`,d,D,h,f),h.isMesh===!0){const A=h.geometry;return M.assignLODInformation(i.url,A,u,e,void 0,O.density),p(A)}else{const A=new Array;for(let C=0;C<h.children.length;C++){const j=h.children[C];if(j instanceof V){const R=j.geometry;M.assignLODInformation(i.url,R,u,e,C,O.density),A.push(R)}}return p(A)}}}return p(null)});return this.previouslyLoaded.set(f,S),await S}else if(t instanceof $){a&&console.log("Load texture from uri: "+d);const f=await new Le().loadAsync(d);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,a&&console.log(l,f)):L&&console.warn("failed loading",d),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 a=new be(t,r,n,o,s);e.userData.LODS=a,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
1
+ var me=Object.defineProperty,pe=(t,e,r)=>e in t?me(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(pe(t,typeof e!="symbol"?e+"":e,r),r);import{MeshoptDecoder as xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as ye}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as ve}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as $,Mesh as G,Material as De,Texture as z,TextureLoader as Le,Matrix4 as ie,Frustum as Me,Sphere as Oe,Box3 as ae,Vector3 as B}from"three";import{GLTFLoader as _e}from"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(t=>{X="./include/draco/",ee="./include/ktx2/"});function we(t){X=t}function be(t){ee=t}let U,te,q;function re(t){U||(U=new ye,U.setDecoderPath(X),U.setDecoderConfig({type:"js"})),q||(q=new ve,q.setTranscoderPath(ee)),te||(te=xe),t?q.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function se(t){t.dracoLoader||t.setDRACOLoader(U),t.ktx2Loader||t.setKTX2Loader(q),t.meshoptDecoder||t.setMeshoptDecoder(te)}function H(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Se(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}let Y;function Te(){return Y!==void 0||(Y=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),H("debugprogressive")&&console.log("isMobileDevice",Y)),Y}function J(t){var e;return((e=t?.userData)==null?void 0:e["needle:raycast-mesh"])instanceof $?t.userData["needle:raycast-mesh"]:null}function le(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!J(t)){const r=Ae(e);r.userData={isRaycastMesh:!0},t.userData||(t.userData={}),t.userData["needle:raycast-mesh"]=r}}function Ee(t=!0){if(t){if(V)return;const e=V=G.prototype.raycast;G.prototype.raycast=function(r,n){const o=this,s=J(o);let i;s&&o.isMesh&&(i=o.geometry,o.geometry=s),e.call(this,r,n),i&&(o.geometry=i)}}else{if(!V)return;G.prototype.raycast=V,V=null}}let V=null;function Ae(t){const e=new $;for(const r in t.attributes)e.setAttribute(r,t.getAttribute(r));return e.setIndex(t.getIndex()),e}const k=new Array,P="NEEDLE_progressive",v=H("debugprogressive"),oe=Symbol("needle-progressive-texture"),K=new Map,ne=new Set;if(v){let t=function(){e+=1,console.log("Toggle LOD level",e,K),K.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,ne&&ne.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=n)}))})}function ue(t,e,r){var n;if(!v)return;K.has(t)||K.set(t,{keys:[],sourceId:r});const o=K.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}),v&&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=this,n="LODS:minmax",o=t[n];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const i of t)this.getMaterialMinMaxLODsCount(i,e);return t[n]=e,e}if(v==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const a of Object.keys(i.uniforms)){const u=i.uniforms[a].value;u?.isTexture===!0&&s(u,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const a=t[i];a?.isTexture===!0&&s(a,e)}return t[n]=e,e;function s(i,a){const u=r.getAssignedLODInformation(i);if(u){const l=r.lodInfos.get(u.key);if(l&&l.lods){a.min_count=Math.min(a.min_count,l.lods.length),a.max_count=Math.max(a.max_count,l.lods.length);for(let g=0;g<l.lods.length;g++){const d=l.lods[g];d.width&&(a.lods[g]=a.lods[g]||{min_height:1/0,max_height:0},a.lods[g].min_height=Math.min(a.lods[g].min_height,d.height),a.lods[g].max_height=Math.max(a.lods[g].max_height,d.height))}}}}}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 G||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 $&&(t.geometry=s,v&&ue(t,"geometry",o.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 De||t.isMaterial===!0){const r=t,n=[],o=new Array;if(v&&ne.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 z||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 v==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,s,o),null}r[n]=o}if(v&&n&&r){const s=this.getAssignedLODInformation(t);s&&ue(r,n,s.url)}}return o}else v=="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 v&&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=v=="verbose",a=t.userData.LODS;if(!a)return null;const u=a?.key;let l;if(t.isTexture===!0){const g=t;g.source&&g.source[oe]&&(l=g.source[oe])}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 g=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!g)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const d=Se(a.url,g);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=d+"_"+l.guid,w=this.previouslyLoaded.get(f);if(w!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let p=await w.catch(N=>(console.error(`Error loading LOD ${e} from ${d}
2
+ `,N),null)),b=!1;if(p==null||(p instanceof z&&t instanceof z?(n=p.image)!=null&&n.data||(o=p.source)!=null&&o.data?p=this.copySettings(t,p):(b=!0,this.previouslyLoaded.delete(f)):p instanceof $&&t instanceof $&&((s=p.attributes.position)!=null&&s.array||(b=!0,this.previouslyLoaded.delete(f)))),!b)return p}const m=l,S=new Promise(async(p,b)=>{const N=new _e;se(N),v&&(await new Promise(y=>setTimeout(y,1e3)),i&&console.warn("Start loading (delayed) "+d,m.guid));let F=d;if(m&&Array.isArray(m.lods)){const y=m.lods[e];y.hash&&(F+="?v="+y.hash)}const x=await N.loadAsync(F).catch(y=>(console.error(`Error loading LOD ${e} from ${d}
3
+ `,y),null));if(!x)return null;const D=x.parser;i&&console.log("Loading finished "+d,m.guid);let L=0;if(x.parser.json.textures){let y=!1;for(const h of x.parser.json.textures){if(h!=null&&h.extensions){const O=h?.extensions[P];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}L++}if(y){let h=await D.getDependency("texture",L);return h&&M.assignLODInformation(a.url,h,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+h.name+'"',d,L,h,f),t instanceof z&&(h=this.copySettings(t,h)),h&&(h.guid=m.guid),p(h)}else v&&console.warn("Could not find texture with guid",m.guid)}if(L=0,x.parser.json.meshes){let y=!1;for(const h of x.parser.json.meshes){if(h!=null&&h.extensions){const O=h?.extensions[P];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}L++}if(y){const h=await D.getDependency("mesh",L),O=m;if(i&&console.log(`Loaded Mesh "${h.name}"`,d,L,h,f),h.isMesh===!0){const A=h.geometry;return M.assignLODInformation(a.url,A,u,e,void 0,O.density),p(A)}else{const A=new Array;for(let C=0;C<h.children.length;C++){const R=h.children[C];if(R instanceof G){const j=R.geometry;M.assignLODInformation(a.url,j,u,e,C,O.density),A.push(j)}}return p(A)}}}return p(null)});return this.previouslyLoaded.set(f,S),await S}else if(t instanceof z){i&&console.log("Load texture from uri: "+d);const f=await new Le().loadAsync(d);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,i&&console.log(l,f)):v&&console.warn("failed loading",d),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,n,o,s){if(!e)return;e.userData||(e.userData={});const i=new Pe(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(),v&&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 _=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 a;L&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const i=r.geometry;i.userData||(i.userData={}),M.assignLODInformation(t,i,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,i);for(const l of k)(a=l.onRegisteredNewMesh)==null||a.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 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&&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 d of i){if(d.material&&(((o=d.geometry)==null?void 0:o.type)==="BoxGeometry"||((s=d.geometry)==null?void 0:s.type)==="BufferGeometry")&&(d.material.name==="SphericalGaussianBlur"||d.material.name=="BackgroundCubeMaterial"||d.material.name==="CubemapFromEquirect"||d.material.name==="EquirectangularToCubeUV")){F&&(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",d,d.material.name,d.material.type)));continue}switch(d.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=d.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}const g=a.transparent;for(const d of g){const f=d.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}}}updateLODs(t,e,r,n,o){var s,a;r.userData||(r.userData={});let i=r.userData.LOD_state;if(i||(i=new Te,r.userData.LOD_state=i),i.frames++<2)return;for(const l of k)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,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)(a=l.onAfterUpdatedLOD)==null||a.call(l,this.renderer,t,e,r,E);i.lastLodLevel_Mesh=E.mesh_lod,i.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 a=10+1;if(F&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=_.getMeshLODInformation(e.geometry),u=i?.lods,l=u&&u.length>0,g=_.getMaterialMinMaxLODsCount(e.material),d=g?.min_count!=1/0&&g.min_count>0&&g.max_count>0;if(!l&&!d){o.mesh_lod=0,o.texture_lod=0;return}if(l||(a=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 x=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(x)){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 x=this._tempBox.min,v=this._tempBox.max;let D=x.x,y=x.y,h=v.x,O=v.y;const A=2,C=1.5,j=(x.x+v.x)*.5,R=(x.y+v.y)*.5;D=(D-j)*A+j,y=(y-R)*A+R,h=(h-j)*A+j,O=(O-R)*A+R;const de=D<0&&h>0?0:Math.min(Math.abs(x.x),Math.abs(v.x)),he=y<0&&O>0?0:Math.min(Math.abs(x.y),Math.abs(v.y)),H=Math.max(de,he);r.lastCentrality=(C-H)*(C-H)*(C-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*=w.aspect;const S=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(S);const b=p.getSize(this._tempBox2Size),G=Math.max(b.x,b.y);if(Math.max(m.x,m.y)!=0&&G!=0&&(m.z=b.z/Math.max(b.x,b.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,F&&T.debugDrawLine){const x=this.tempMatrix.copy(this.projectionScreenMatrix);x.invert();const v=T.corner0,D=T.corner1,y=T.corner2,h=T.corner3;v.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=v.x,y.copy(this._tempBox.max),y.y=v.y,h.copy(this._tempBox.max);const O=(v.z+h.z)*.5;v.z=D.z=y.z=h.z=O,v.applyMatrix4(x),D.applyMatrix4(x),y.applyMatrix4(x),h.applyMatrix4(x),T.debugDrawLine(v,D,255),T.debugDrawLine(v,y,255),T.debugDrawLine(D,h,255),T.debugDrawLine(y,h,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let x=0;x<u.length;x++)if(u[x].density/r.lastScreenCoverage<n){N=x;break}}N<a&&(a=N)}if(o.mesh_lod=a,d)if(r.lastLodLevel_Texture<0){if(o.texture_lod=g.max_count-1,F){const w=g.lods[g.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,m=this.renderer.domElement.clientHeight/window.devicePixelRatio*w;for(let S=g.lods.length-1;S>=0;S--){const p=g.lods[S];if(p.max_height>m){o.texture_lod=S,o.texture_lod<r.lastLodLevel_Texture&&F&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} (${p.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${m.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(i=>i.toString()=="Symbol(renderer)"),a=o.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 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(i){var u,l,g;if(i[K]==!0)return;i[K]=!0,i.userData&&(i.userData.LOD=-1);const d=Object.keys(i);for(let f=0;f<d.length;f++){const w=d[f],m=i[w];if(m?.isTexture===!0){const S=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=n.parser.json.textures[S];if(!p){console.warn("Texture data not found for texture index "+S);continue}if((g=p?.extensions)!=null&&g[P]){const b=p.extensions[P];b&&o&&_.registerTexture(o,m,b.lods.length,S,b)}}}};const a=r.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,r){var n,o;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const a=(o=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[P];if(a&&s){const i=r.uuid;_.registerMesh(s,i,r,0,a.lods.length,a)}}}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};
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)=>{v&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,o),e.source&&(e.source[oe]=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;v&&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&&!J(r)&&le(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 Pe{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 W=H("debugprogressive"),Ie=H("noprogressive"),ce=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},T=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new ie),c(this,"cameraFrustrum",new Me),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new Oe),c(this,"_tempBox",new ae),c(this,"_tempBox2",new ae),c(this,"tempMatrix",new ie),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[ce]?t[ce]:new T(t)}get plugins(){return k}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;re(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(Ie||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 d of a){if(d.material&&(((o=d.geometry)==null?void 0:o.type)==="BoxGeometry"||((s=d.geometry)==null?void 0:s.type)==="BufferGeometry")&&(d.material.name==="SphericalGaussianBlur"||d.material.name=="BackgroundCubeMaterial"||d.material.name==="CubemapFromEquirect"||d.material.name==="EquirectangularToCubeUV")){W&&(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",d,d.material.name,d.material.type)));continue}switch(d.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=d.object;(f instanceof G||f.isMesh)&&this.updateLODs(t,e,f,l,n)}const g=i.transparent;for(const d of g){const f=d.object;(f instanceof G||f.isMesh)&&this.updateLODs(t,e,f,l,n)}}}updateLODs(t,e,r,n,o){var s,i;r.userData||(r.userData={});let a=r.userData.LOD_state;if(a||(a=new Be,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(W&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=_.getMeshLODInformation(e.geometry),u=a?.lods,l=u&&u.length>0,g=_.getMaterialMinMaxLODsCount(e.material),d=g?.min_count!=1/0&&g.min_count>0&&g.max_count>0;if(!l&&!d){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 x=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(x)){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 x=this._tempBox.min,D=this._tempBox.max;let L=x.x,y=x.y,h=D.x,O=D.y;const A=2,C=1.5,R=(x.x+D.x)*.5,j=(x.y+D.y)*.5;L=(L-R)*A+R,y=(y-j)*A+j,h=(h-R)*A+R,O=(O-j)*A+j;const fe=L<0&&h>0?0:Math.min(Math.abs(x.x),Math.abs(D.x)),ge=y<0&&O>0?0:Math.min(Math.abs(x.y),Math.abs(D.y)),Z=Math.max(fe,ge);r.lastCentrality=(C-Z)*(C-Z)*(C-Z)}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*=w.aspect;const S=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(S);const b=p.getSize(this._tempBox2Size),N=Math.max(b.x,b.y);if(Math.max(m.x,m.y)!=0&&N!=0&&(m.z=b.z/Math.max(b.x,b.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,W&&T.debugDrawLine){const x=this.tempMatrix.copy(this.projectionScreenMatrix);x.invert();const D=T.corner0,L=T.corner1,y=T.corner2,h=T.corner3;D.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=D.x,y.copy(this._tempBox.max),y.y=D.y,h.copy(this._tempBox.max);const O=(D.z+h.z)*.5;D.z=L.z=y.z=h.z=O,D.applyMatrix4(x),L.applyMatrix4(x),y.applyMatrix4(x),h.applyMatrix4(x),T.debugDrawLine(D,L,255),T.debugDrawLine(D,y,255),T.debugDrawLine(L,h,255),T.debugDrawLine(y,h,255)}let F=999;if(u&&r.lastScreenCoverage>0){for(let x=0;x<u.length;x++)if(u[x].density/r.lastScreenCoverage<n){F=x;break}}F<i&&(i=F)}if(o.mesh_lod=i,d)if(r.lastLodLevel_Texture<0){if(o.texture_lod=g.max_count-1,W){const w=g.lods[g.max_count-1];W&&console.log(`First Texture LOD ${o.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenCoverage*1.5,m=this.renderer.domElement.clientHeight/window.devicePixelRatio*w;for(let S=g.lods.length-1;S>=0;S--){const p=g.lods[S];if(!(Te()&&p.max_height>4096)&&p.max_height>m){o.texture_lod=S,o.texture_lod<r.lastLodLevel_Texture&&W&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} (${p.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${m.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 Be{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 de=Symbol("NEEDLE_mesh_lod"),Q=Symbol("NEEDLE_texture_lod");function he(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 Ce(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 Ce{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[Q]==!0)return;r[Q]=!0;const n=this.tryGetCurrentGLTF(e),o=this.getUrl();if(o&&n&&r.material){let s=function(a){var u,l,g;if(a[Q]==!0)return;a[Q]=!0,a.userData&&(a.userData.LOD=-1);const d=Object.keys(a);for(let f=0;f<d.length;f++){const w=d[f],m=a[w];if(m?.isTexture===!0){const S=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=n.parser.json.textures[S];if(!p){console.warn("Texture data not found for texture index "+S);continue}if((g=p?.extensions)!=null&&g[P]){const b=p.extensions[P];b&&o&&_.registerTexture(o,m,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[de]==!0)return;r[de]=!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 ke(t,e,r,n){re(e),se(r),r.register(s=>new _(s,t));const o=I.get(e);return n?.enableLODsManager!==!1&&o.enable(),o}document.addEventListener("DOMContentLoaded",()=>{he(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,I as LODsManager,_ as NEEDLE_progressive,se as addDracoAndKTX2Loaders,re as createLoaders,J as getRaycastMesh,he as patchModelViewer,we as setDracoDecoderLocation,be as setKTX2TranscoderLocation,le as setRaycastMesh,ke as useNeedleProgressive,Ee as useRaycastMeshes};
@@ -1,5 +1,5 @@
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"),p=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 p.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),h=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,h),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=this,i="LODS:minmax",o=e[i];if(o!=null)return o;if(t||(t={min_count:1/0,max_count:0,lods:[]}),Array.isArray(e)){for(const n of e)this.getMaterialMinMaxLODsCount(n,t);return e[i]=t,t}if(w==="verbose"&&console.log("getMaterialMinMaxLODsCount",e),e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const n=e;for(const a of Object.keys(n.uniforms)){const h=n.uniforms[a].value;(h==null?void 0:h.isTexture)===!0&&s(h,t)}}else if(e.isMaterial)for(const n of Object.keys(e)){const a=e[n];(a==null?void 0:a.isTexture)===!0&&s(a,t)}return e[i]=t,t;function s(n,a){const h=r.getAssignedLODInformation(n);if(h){const f=r.lodInfos.get(h.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let g=0;g<f.lods.length;g++){const d=f.lods[g];d.width&&(a.lods[g]=a.lods[g]||{min_height:1/0,max_height:0},a.lods[g].min_height=Math.min(a.lods[g].min_height,d.height),a.lods[g].max_height=Math.max(a.lods[g].max_height,d.height))}}}}}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 p.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 p.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 p.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 h=this.assignTextureLODForSlot(a,t,r,n);i.push(h),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 h=s[a],f=o[a];h&&h.isTexture===!0?n.push({material:r,slot:f,texture:h,level:t}):n.push({material:r,slot:f,texture:null,level:t})}return n})}if(e instanceof p.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,h,f;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 g=e;g.source&&g.source[Q]&&(s=g.source[Q])}if(s||(s=v.lodInfos.get(o)),s){if(t>0){let x=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?x=!0:M||(x=!0),x)return this.lowresCache.get(o)}const g=Array.isArray(s.lods)?(n=s.lods[t])==null?void 0:n.path:s.lods;if(!g)return w&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const d=De(i.url,g);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const x=d+"_"+s.guid,M=this.previouslyLoaded.get(x);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${x}`);let m=await M.catch(F=>(console.error(`Error loading LOD ${t} from ${d}
2
- `,F),null)),A=!1;if(m==null||(m instanceof p.Texture&&e instanceof p.Texture?(a=m.image)!=null&&a.data||(h=m.source)!=null&&h.data?m=this.copySettings(e,m):(A=!0,this.previouslyLoaded.delete(x)):m instanceof p.BufferGeometry&&e instanceof p.BufferGeometry&&((f=m.attributes.position)!=null&&f.array||(A=!0,this.previouslyLoaded.delete(x)))),!A)return m}const O=s,_=new Promise(async(m,A)=>{const F=new Le.GLTFLoader;re(F),w&&(await new Promise(y=>setTimeout(y,1e3)),r&&console.warn("Start loading (delayed) "+d,O.guid));let Y=d;if(O&&Array.isArray(O.lods)){const y=O.lods[t];y.hash&&(Y+="?v="+y.hash)}const B=await F.loadAsync(Y).catch(y=>(console.error(`Error loading LOD ${t} from ${d}
3
- `,y),null));if(!B)return null;const H=B.parser;r&&console.log("Loading finished "+d,O.guid);let L=0;if(B.parser.json.textures){let y=!1;for(const u of B.parser.json.textures){if(u!=null&&u.extensions){const D=u==null?void 0:u.extensions[R];if(D!=null&&D.guid&&D.guid===O.guid){y=!0;break}}L++}if(y){let u=await H.getDependency("texture",L);return u&&v.assignLODInformation(i.url,u,o,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+u.name+'"',d,L,u,x),e instanceof p.Texture&&(u=this.copySettings(e,u)),u&&(u.guid=O.guid),m(u)}else w&&console.warn("Could not find texture with guid",O.guid)}if(L=0,B.parser.json.meshes){let y=!1;for(const u of B.parser.json.meshes){if(u!=null&&u.extensions){const D=u==null?void 0:u.extensions[R];if(D!=null&&D.guid&&D.guid===O.guid){y=!0;break}}L++}if(y){const u=await H.getDependency("mesh",L),D=O;if(r&&console.log(`Loaded Mesh "${u.name}"`,d,L,u,x),u.isMesh===!0){const T=u.geometry;return v.assignLODInformation(i.url,T,o,t,void 0,D.density),m(T)}else{const T=new Array;for(let C=0;C<u.children.length;C++){const G=u.children[C];if(G instanceof p.Mesh){const N=G.geometry;v.assignLODInformation(i.url,N,o,t,C,D.density),T.push(N)}}return m(T)}}}return m(null)});return this.previouslyLoaded.set(x,_),await _}else if(e instanceof p.Texture){r&&console.log("Load texture from uri: "+d);const M=await new p.TextureLoader().loadAsync(d);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",d),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
1
+ "use strict";var ge=Object.defineProperty;var pe=(l,e,t)=>e in l?ge(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var c=(l,e,t)=>(pe(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ye=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Le=require("three/examples/jsm/loaders/DRACOLoader.js"),xe=require("three/examples/jsm/loaders/KTX2Loader.js"),g=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js");let H="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ie="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(H+"draco_decoder.js",{method:"head"}).catch(l=>{H="./include/draco/",ie="./include/ktx2/"});function De(l){H=l}function Me(l){ie=l}let W,re,V;function oe(l){W||(W=new Le.DRACOLoader,W.setDecoderPath(H),W.setDecoderConfig({type:"js"})),V||(V=new xe.KTX2Loader,V.setTranscoderPath(ie)),re||(re=ye.MeshoptDecoder),l?V.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function ne(l){l.dracoLoader||l.setDRACOLoader(W),l.ktx2Loader||l.setKTX2Loader(V),l.meshoptDecoder||l.setMeshoptDecoder(re)}function J(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function _e(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}let $;function Oe(){return $!==void 0||($=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),J("debugprogressive")&&console.log("isMobileDevice",$)),$}function Q(l){var e;return((e=l==null?void 0:l.userData)==null?void 0:e["needle:raycast-mesh"])instanceof g.BufferGeometry?l.userData["needle:raycast-mesh"]:null}function ue(l,e){if((l.type==="Mesh"||l.type==="SkinnedMesh")&&!Q(l)){const r=ve(e);r.userData={isRaycastMesh:!0},l.userData||(l.userData={}),l.userData["needle:raycast-mesh"]=r}}function we(l=!0){if(l){if(U)return;const e=U=g.Mesh.prototype.raycast;g.Mesh.prototype.raycast=function(t,r){const s=this,o=Q(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),e.call(this,t,r),i&&(s.geometry=i)}}else{if(!U)return;g.Mesh.prototype.raycast=U,U=null}}let U=null;function ve(l){const e=new g.BufferGeometry;for(const t in l.attributes)e.setAttribute(t,l.getAttribute(t));return e.setIndex(l.getIndex()),e}const I=new Array,R="NEEDLE_progressive",w=J("debugprogressive"),te=Symbol("needle-progressive-texture"),q=new Map,se=new Set;if(w){let l=function(){e+=1,console.log("Toggle LOD level",e,q),q.forEach((s,o)=>{for(const i of s.keys){const n=o[i];if(n.isBufferGeometry===!0){const a=S.getMeshLODInformation(n),h=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,h),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",s=>{s.key==="p"&&l(),s.key==="w"&&(r=!r,se&&se.forEach(o=>{o.name!="BackgroundCubeMaterial"&&"wireframe"in o&&(o.wireframe=r)}))})}function ae(l,e,t){var s;if(!w)return;q.has(l)||q.set(l,{keys:[],sourceId:t});const r=q.get(l);((s=r==null?void 0:r.keys)==null?void 0:s.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,s;if(this._isLoadingMesh)return null;const t=(s=(r=this.parser.json.meshes[e])==null?void 0:r.extensions)==null?void 0:s[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=this,s="LODS:minmax",o=e[s];if(o!=null)return o;if(t||(t={min_count:1/0,max_count:0,lods:[]}),Array.isArray(e)){for(const n of e)this.getMaterialMinMaxLODsCount(n,t);return e[s]=t,t}if(w==="verbose"&&console.log("getMaterialMinMaxLODsCount",e),e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const n=e;for(const a of Object.keys(n.uniforms)){const h=n.uniforms[a].value;(h==null?void 0:h.isTexture)===!0&&i(h,t)}}else if(e.isMaterial)for(const n of Object.keys(e)){const a=e[n];(a==null?void 0:a.isTexture)===!0&&i(a,t)}return e[s]=t,t;function i(n,a){const h=r.getAssignedLODInformation(n);if(h){const d=r.lodInfos.get(h.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let p=0;p<d.lods.length;p++){const f=d.lods[p];f.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,f.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,f.height))}}}}}static hasLODLevelAvailable(e,t){var o;if(Array.isArray(e)){for(const i of e)if(this.hasLODLevelAvailable(i,t))return!0;return!1}if(e.isMaterial===!0){for(const i of Object.keys(e)){const n=e[i];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,t))return!0}return!1}else if(e.isGroup===!0){for(const i of e.children)if(i.isMesh===!0&&this.hasLODLevelAvailable(i,t))return!0}let r,s;if(e.isMesh?r=e.geometry:(e.isBufferGeometry||e.isTexture)&&(r=e),r&&(o=r==null?void 0:r.userData)!=null&&o.LODS){const i=r.userData.LODS;if(s=this.lodInfos.get(i.key),t===void 0)return s!=null;if(s)return Array.isArray(s.lods)?t<s.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 s=e.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const i of I)(r=i.onBeforeGetLODMesh)==null||r.call(i,e,t);return e["LOD:requested level"]=t,v.getOrLoadLOD(s,t).then(i=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(i)){const n=o.index||0;i=i[n]}i&&s!=i&&i instanceof g.BufferGeometry&&(e.geometry=i,w&&ae(e,"geometry",o.url))}return i}).catch(i=>(console.error("Error loading mesh LOD",e,i),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,s=[],o=new Array;if(w&&se.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const i=r;for(const n of Object.keys(i.uniforms)){const a=i.uniforms[n].value;if((a==null?void 0:a.isTexture)===!0){const h=this.assignTextureLODForSlot(a,t,r,n);s.push(h),o.push(n)}}}else for(const i of Object.keys(r)){const n=r[i];if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,i);s.push(a),o.push(i)}}return Promise.all(s).then(i=>{const n=new Array;for(let a=0;a<i.length;a++){const h=i[a],d=o[a];h&&h.isTexture===!0?n.push({material:r,slot:d,texture:h,level:t}):n.push({material:r,slot:d,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,s){return(e==null?void 0:e.isTexture)!==!0?Promise.resolve(null):s==="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&&s){const i=r[s];if(i){const n=this.getAssignedLODInformation(i);if(n&&(n==null?void 0:n.level)<t)return w==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,t,r,i,o),null}r[s]=o}if(w&&s&&r){const i=this.getAssignedLODInformation(e);i&&ae(r,s,i.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((s,o)=>{if(s!=null&&s.extensions){const i=s==null?void 0:s.extensions[R];if(i){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,i.lods.length,o,i));n||this.parser.getDependency("texture",o).then(a=>{a&&v.registerTexture(this.url,a,i.lods.length,o,i)})}}}),(r=this.parser.json.meshes)==null||r.forEach((s,o)=>{if(s!=null&&s.extensions){const i=s==null?void 0:s.extensions[R];if(i&&i.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,i.guid,n,i.lods.length,a.primitives,i)}}}}),null}static async getOrLoadLOD(e,t){var n,a,h,d;const r=w=="verbose",s=e.userData.LODS;if(!s)return null;const o=s==null?void 0:s.key;let i;if(e.isTexture===!0){const p=e;p.source&&p.source[te]&&(i=p.source[te])}if(i||(i=v.lodInfos.get(o)),i){if(t>0){let x=!1;const M=Array.isArray(i.lods);if(M&&t>=i.lods.length?x=!0:M||(x=!0),x)return this.lowresCache.get(o)}const p=Array.isArray(i.lods)?(n=i.lods[t])==null?void 0:n.path:i.lods;if(!p)return w&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,i)),null;const f=_e(s.url,p);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const x=f+"_"+i.guid,M=this.previouslyLoaded.get(x);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${x}`);let m=await M.catch(F=>(console.error(`Error loading LOD ${t} from ${f}
2
+ `,F),null)),A=!1;if(m==null||(m instanceof g.Texture&&e instanceof g.Texture?(a=m.image)!=null&&a.data||(h=m.source)!=null&&h.data?m=this.copySettings(e,m):(A=!0,this.previouslyLoaded.delete(x)):m instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((d=m.attributes.position)!=null&&d.array||(A=!0,this.previouslyLoaded.delete(x)))),!A)return m}const _=i,O=new Promise(async(m,A)=>{const F=new me.GLTFLoader;ne(F),w&&(await new Promise(y=>setTimeout(y,1e3)),r&&console.warn("Start loading (delayed) "+f,_.guid));let Z=f;if(_&&Array.isArray(_.lods)){const y=_.lods[t];y.hash&&(Z+="?v="+y.hash)}const B=await F.loadAsync(Z).catch(y=>(console.error(`Error loading LOD ${t} from ${f}
3
+ `,y),null));if(!B)return null;const j=B.parser;r&&console.log("Loading finished "+f,_.guid);let L=0;if(B.parser.json.textures){let y=!1;for(const u of B.parser.json.textures){if(u!=null&&u.extensions){const D=u==null?void 0:u.extensions[R];if(D!=null&&D.guid&&D.guid===_.guid){y=!0;break}}L++}if(y){let u=await j.getDependency("texture",L);return u&&v.assignLODInformation(s.url,u,o,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+u.name+'"',f,L,u,x),e instanceof g.Texture&&(u=this.copySettings(e,u)),u&&(u.guid=_.guid),m(u)}else w&&console.warn("Could not find texture with guid",_.guid)}if(L=0,B.parser.json.meshes){let y=!1;for(const u of B.parser.json.meshes){if(u!=null&&u.extensions){const D=u==null?void 0:u.extensions[R];if(D!=null&&D.guid&&D.guid===_.guid){y=!0;break}}L++}if(y){const u=await j.getDependency("mesh",L),D=_;if(r&&console.log(`Loaded Mesh "${u.name}"`,f,L,u,x),u.isMesh===!0){const T=u.geometry;return v.assignLODInformation(s.url,T,o,t,void 0,D.density),m(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(s.url,N,o,t,C,D.density),T.push(N)}}return m(T)}}}return m(null)});return this.previouslyLoaded.set(x,O),await O}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=i.guid,M.flipY=!1,M.needsUpdate=!0,M.colorSpace=e.colorSpace,r&&console.log(i,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,s,o,i){if(!t)return;t.userData||(t.userData={});const n=new Se(e,r,s,o,i);t.userData.LODS=n,t.userData.LOD=s}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;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 h;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 f of I)(h=f.onRegisteredNewMesh)==null||h.call(f,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 p.Matrix4);c(this,"cameraFrustrum",new p.Frustum);c(this,"targetTriangleDensity",2e5);c(this,"updateInterval",0);c(this,"pause",!1);c(this,"_frame",0);c(this,"_originalRender");c(this,"_sphere",new p.Sphere);c(this,"_tempBox",new p.Box3);c(this,"_tempBox2",new p.Box3);c(this,"tempMatrix",new p.Matrix4);c(this,"_tempWorldPosition",new p.Vector3);c(this,"_tempBoxSize",new p.Vector3);c(this,"_tempBox2Size",new p.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,h;if(this.pause)return;const o=this.renderer.renderLists.get(e,0),s=o.opaque;let n=!0;if(s.length===1){const f=s[0].material;(f.name==="EffectMaterial"||f.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 f=this.targetTriangleDensity;for(const d of s){if(d.material&&(((a=d.geometry)==null?void 0:a.type)==="BoxGeometry"||((h=d.geometry)==null?void 0:h.type)==="BufferGeometry")&&(d.material.name==="SphericalGaussianBlur"||d.material.name=="BackgroundCubeMaterial"||d.material.name==="CubemapFromEquirect"||d.material.name==="EquirectangularToCubeUV")){z&&(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",d,d.material.name,d.material.type)));continue}switch(d.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const x=d.object;(x instanceof p.Mesh||x.isMesh)&&this.updateLODs(e,t,x,f,i)}const g=o.transparent;for(const d of g){const x=d.object;(x instanceof p.Mesh||x.isMesh)&&this.updateLODs(e,t,x,f,i)}}}updateLODs(e,t,r,i,o){var a,h;r.userData||(r.userData={});let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const f of I)(a=f.onBeforeUpdateLOD)==null||a.call(f,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 f=r["DEBUG:LOD"];f!=null&&(n=f),this.loadProgressiveTextures(r.material,n)}for(const f of I)(h=f.onAfterUpdatedLOD)==null||h.call(f,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),h=a==null?void 0:a.lods,f=h&&h.length>0,g=S.getMaterialMinMaxLODsCount(t.material),d=(g==null?void 0:g.min_count)!=1/0&&g.min_count>0&&g.max_count>0;if(!f&&!d){o.mesh_lod=0,o.texture_lod=0;return}if(f||(n=0),!((M=this.cameraFrustrum)!=null&&M.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const x=t.geometry.boundingBox;if(x&&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 L=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(L)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(x),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 L=this._tempBox.min,y=this._tempBox.max;let u=L.x,D=L.y,T=y.x,C=y.y;const G=2,N=1.5,V=(L.x+y.x)*.5,q=(L.y+y.y)*.5;u=(u-V)*G+V,D=(D-q)*G+q,T=(T-V)*G+V,C=(C-q)*G+q;const ue=u<0&&T>0?0:Math.min(Math.abs(L.x),Math.abs(y.x)),de=D<0&&C>0?0:Math.min(Math.abs(L.y),Math.abs(y.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,m=this._tempBox2;m.copy(x),m.applyMatrix4(t.matrixWorld),m.applyMatrix4(k);const A=m.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 L=this.tempMatrix.copy(this.projectionScreenMatrix);L.invert();const y=b.corner0,u=b.corner1,D=b.corner2,T=b.corner3;y.copy(this._tempBox.min),u.copy(this._tempBox.max),u.x=y.x,D.copy(this._tempBox.max),D.y=y.y,T.copy(this._tempBox.max);const C=(y.z+T.z)*.5;y.z=u.z=D.z=T.z=C,y.applyMatrix4(L),u.applyMatrix4(L),D.applyMatrix4(L),T.applyMatrix4(L),b.debugDrawLine(y,u,255),b.debugDrawLine(y,D,255),b.debugDrawLine(u,T,255),b.debugDrawLine(D,T,255)}let B=999;if(h&&r.lastScreenCoverage>0){for(let L=0;L<h.length;L++)if(h[L].density/r.lastScreenCoverage<i){B=L;break}}B<n&&(n=B)}if(o.mesh_lod=n,d)if(r.lastLodLevel_Texture<0){if(o.texture_lod=g.max_count-1,z){const O=g.lods[g.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 m=g.lods.length-1;m>=0;m--){const A=g.lods[m];if(A.max_height>k){o.texture_lod=m,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 p.Vector3),c(P,"corner1",new p.Vector3),c(P,"corner2",new p.Vector3),c(P,"corner3",new p.Vector3),c(P,"_tempPtInside",new p.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 p.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 h,f,g;if(n[K]==!0)return;n[K]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let d=0;d<a.length;d++){const x=a[d],M=n[x];if((M==null?void 0:M.isTexture)===!0){const O=(f=(h=M.userData)==null?void 0:h.associations)==null?void 0:f.textures,_=r.parser.json.textures[O];if(!_){console.warn("Texture data not found for texture index "+O);continue}if((g=_==null?void 0:_.extensions)!=null&&g[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;
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,s,o)=>{w&&console.log("> Progressive: register texture",s,t.name,t.uuid,t,o),t.source&&(t.source[te]=o);const i=o.guid;v.assignLODInformation(e,t,i,r,s,void 0),v.lodInfos.set(i,o),v.lowresCache.set(i,t)}),c(S,"registerMesh",(e,t,r,s,o,i)=>{var h;w&&console.log("> Progressive: register mesh",o,r.name,i,r.uuid,r);const n=r.geometry;n.userData||(n.userData={}),v.assignLODInformation(e,n,t,s,o,i.density),v.lodInfos.set(t,i);let a=v.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],v.lowresCache.set(t,a),s>0&&!Q(r)&&ue(r,n);for(const d of I)(h=d.onRegisteredNewMesh)==null||h.call(d,r,i)}),c(S,"lodInfos",new Map),c(S,"previouslyLoaded",new Map),c(S,"lowresCache",new Map);class Se{constructor(e,t,r,s,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,s!=null&&(this.index=s),o!=null&&(this.density=o)}}const z=J("debugprogressive"),Te=J("noprogressive"),le=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[le]?e[le]:new b(e)}get plugins(){return I}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;oe(this.renderer),this.renderer.render=function(r,s){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const i=t._frame,n=e++;t.onBeforeRender(r,s,n,i),t._originalRender.call(this,r,s),t.onAfterRender(r,s,n,i)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,s){}onAfterRender(e,t,r,s){var a,h;if(this.pause)return;const o=this.renderer.renderLists.get(e,0),i=o.opaque;let n=!0;if(i.length===1){const d=i[0].material;(d.name==="EffectMaterial"||d.name==="CopyShader")&&(n=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(n=!1),n){if(Te||this.updateInterval>0&&s%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const d=this.targetTriangleDensity;for(const f of i){if(f.material&&(((a=f.geometry)==null?void 0:a.type)==="BoxGeometry"||((h=f.geometry)==null?void 0:h.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 x=f.object;(x instanceof g.Mesh||x.isMesh)&&this.updateLODs(e,t,x,d,s)}const p=o.transparent;for(const f of p){const x=f.object;(x instanceof g.Mesh||x.isMesh)&&this.updateLODs(e,t,x,d,s)}}}updateLODs(e,t,r,s,o){var a,h;r.userData||(r.userData={});let i=r.userData.LOD_state;if(i||(i=new Ae,r.userData.LOD_state=i),i.frames++<2)return;for(const d of I)(a=d.onBeforeUpdateLOD)==null||a.call(d,this.renderer,e,t,r);this.calculateLodLevel(t,r,i,s,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 d=r["DEBUG:LOD"];d!=null&&(n=d),this.loadProgressiveTextures(r.material,n)}for(const d of I)(h=d.onAfterUpdatedLOD)==null||h.call(d,this.renderer,e,t,r,E);i.lastLodLevel_Mesh=E.mesh_lod,i.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 s=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(s=!0),s&&(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(s=>(s&&e.userData.LOD==t&&r!=e.geometry,s))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,s=e.max,o=(r.x+s.x)*.5,i=(r.y+s.y)*.5;return this._tempPtInside.set(o,i,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,s,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),h=a==null?void 0:a.lods,d=h&&h.length>0,p=S.getMaterialMinMaxLODsCount(t.material),f=(p==null?void 0:p.min_count)!=1/0&&p.min_count>0&&p.max_count>0;if(!d&&!f){o.mesh_lod=0,o.texture_lod=0;return}if(d||(n=0),!((M=this.cameraFrustrum)!=null&&M.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const x=t.geometry.boundingBox;if(x&&e.isPerspectiveCamera){const _=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const L=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(L)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(x),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&&_.fov>70){const L=this._tempBox.min,y=this._tempBox.max;let u=L.x,D=L.y,T=y.x,C=y.y;const G=2,N=1.5,K=(L.x+y.x)*.5,X=(L.y+y.y)*.5;u=(u-K)*G+K,D=(D-X)*G+X,T=(T-K)*G+K,C=(C-X)*G+X;const de=u<0&&T>0?0:Math.min(Math.abs(L.x),Math.abs(y.x)),he=D<0&&C>0?0:Math.min(Math.abs(L.y),Math.abs(y.y)),ee=Math.max(de,he);r.lastCentrality=(N-ee)*(N-ee)*(N-ee)}else r.lastCentrality=1;const O=this._tempBox.getSize(this._tempBoxSize);O.multiplyScalar(.5),screen.availHeight>0&&O.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),O.x*=_.aspect;const k=e.matrixWorldInverse,m=this._tempBox2;m.copy(x),m.applyMatrix4(t.matrixWorld),m.applyMatrix4(k);const A=m.getSize(this._tempBox2Size),F=Math.max(A.x,A.y);if(Math.max(O.x,O.y)!=0&&F!=0&&(O.z=A.z/Math.max(A.x,A.y)*Math.max(O.x,O.y)),r.lastScreenCoverage=Math.max(O.x,O.y,O.z),r.lastScreenspaceVolume.copy(O),r.lastScreenCoverage*=r.lastCentrality,z&&b.debugDrawLine){const L=this.tempMatrix.copy(this.projectionScreenMatrix);L.invert();const y=b.corner0,u=b.corner1,D=b.corner2,T=b.corner3;y.copy(this._tempBox.min),u.copy(this._tempBox.max),u.x=y.x,D.copy(this._tempBox.max),D.y=y.y,T.copy(this._tempBox.max);const C=(y.z+T.z)*.5;y.z=u.z=D.z=T.z=C,y.applyMatrix4(L),u.applyMatrix4(L),D.applyMatrix4(L),T.applyMatrix4(L),b.debugDrawLine(y,u,255),b.debugDrawLine(y,D,255),b.debugDrawLine(u,T,255),b.debugDrawLine(D,T,255)}let B=999;if(h&&r.lastScreenCoverage>0){for(let L=0;L<h.length;L++)if(h[L].density/r.lastScreenCoverage<s){B=L;break}}B<n&&(n=B)}if(o.mesh_lod=n,f)if(r.lastLodLevel_Texture<0){if(o.texture_lod=p.max_count-1,z){const _=p.lods[p.max_count-1];z&&console.log(`First Texture LOD ${o.texture_lod} (${_.max_height}px) - ${t.name}`)}}else{const _=r.lastScreenCoverage*1.5,k=this.renderer.domElement.clientHeight/window.devicePixelRatio*_;for(let m=p.lods.length-1;m>=0;m--){const A=p.lods[m];if(!(Oe()&&A.max_height>4096)&&A.max_height>k){o.texture_lod=m,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 Ae{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 ce=Symbol("NEEDLE_mesh_lod"),Y=Symbol("NEEDLE_texture_lod");function fe(l){if(!l)return null;let e=null,t=null;for(let r=l;r!=null;r=Object.getPrototypeOf(r)){const s=Object.getOwnPropertySymbols(r),o=s.find(n=>n.toString()=="Symbol(renderer)"),i=s.find(n=>n.toString()=="Symbol(scene)");!e&&o!=null&&(e=l[o].threeRenderer),!t&&i!=null&&(t=l[i])}if(e){console.log("Adding Needle LODs to modelviewer");const r=P.get(e);if(P.addPlugin(new be(l)),r.enable(),t){const s=t.camera||t.traverse(o=>o.type=="PerspectiveCamera")[0];s&&e.render(t,s)}return()=>{r.disable()}}return null}class be{constructor(e){c(this,"modelviewer");c(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,s){this.tryParseMeshLOD(t,s),this.tryParseTextureLOD(t,s)}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[Y]==!0)return;t[Y]=!0;const r=this.tryGetCurrentGLTF(e),s=this.getUrl();if(s&&r&&t.material){let o=function(n){var h,d,p;if(n[Y]==!0)return;n[Y]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let f=0;f<a.length;f++){const x=a[f],M=n[x];if((M==null?void 0:M.isTexture)===!0){const _=(d=(h=M.userData)==null?void 0:h.associations)==null?void 0:d.textures,O=r.parser.json.textures[_];if(!O){console.warn("Texture data not found for texture index "+_);continue}if((p=O==null?void 0:O.extensions)!=null&&p[R]){const k=O.extensions[R];k&&s&&S.registerTexture(s,M,k.lods.length,_,k)}}}};const i=t.material;if(Array.isArray(i))for(const n of i)o(n);else o(i)}}tryParseMeshLOD(e,t){var o,i;if(t[ce]==!0)return;t[ce]=!0;const r=this.getUrl();if(!r)return;const s=(i=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:i[R];if(s&&r){const n=t.uuid;S.registerMesh(r,n,t,0,s.lods.length,s)}}}function Pe(l,e,t,r){oe(e),ne(t),t.register(o=>new S(o,l));const s=P.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&s.enable(),s}document.addEventListener("DOMContentLoaded",()=>{fe(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=R;exports.LODsManager=P;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=ne;exports.createLoaders=oe;exports.getRaycastMesh=Q;exports.patchModelViewer=fe;exports.setDracoDecoderLocation=De;exports.setKTX2TranscoderLocation=Me;exports.setRaycastMesh=ue;exports.useNeedleProgressive=Pe;exports.useRaycastMeshes=we;
package/lib/extension.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { BufferGeometry, Material, Mesh, Texture, TextureLoader } from "three";
2
2
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { addDracoAndKTX2Loaders } from "./loaders.js";
4
- import { getParam, getRaycastMesh, resolveUrl, setRaycastMesh } from "./utils.js";
4
+ import { getParam, resolveUrl } from "./utils.internal.js";
5
+ import { getRaycastMesh, setRaycastMesh } from "./utils.js";
5
6
  // All of this has to be removed
6
7
  // import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
7
8
  // import { PromiseAllWithErrors, resolveUrl } from "../../engine_utils.js";
package/lib/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export * from "./extension.js";
5
5
  export * from "./plugins/index.js";
6
6
  export { LODsManager } from "./lods_manager.js";
7
7
  export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
8
- export { getRaycastMesh, setRaycastMesh } from "./utils.js";
8
+ export * from "./utils.js";
9
9
  declare class UseNeedleGLTFProgressive {
10
10
  enableLODsManager?: boolean;
11
11
  }
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@ export * from "./extension.js";
5
5
  export * from "./plugins/index.js";
6
6
  export { LODsManager } from "./lods_manager.js";
7
7
  export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
8
- export { getRaycastMesh, setRaycastMesh } from "./utils.js";
8
+ export * from "./utils.js";
9
9
  ;
10
10
  /** Use this function to enable progressive loading of gltf models.
11
11
  * @param url The url of the gltf model.
@@ -1,7 +1,7 @@
1
1
  import { Box3, Frustum, Matrix4, Mesh, Sphere, Vector3 } from "three";
2
2
  import { NEEDLE_progressive } from "./extension.js";
3
3
  import { createLoaders } from "./loaders.js";
4
- import { getParam } from "./utils.js";
4
+ import { getParam, isMobileDevice } from "./utils.internal.js";
5
5
  import { plugins } from "./plugins/plugin.js";
6
6
  const debugProgressiveLoading = getParam("debugprogressive");
7
7
  const suppressProgressiveLoading = getParam("noprogressive");
@@ -505,6 +505,8 @@ export class LODsManager {
505
505
  const pixelSizeOnScreen = screenSize * factor;
506
506
  for (let i = texture_lods_minmax.lods.length - 1; i >= 0; i--) {
507
507
  const lod = texture_lods_minmax.lods[i];
508
+ if (isMobileDevice() && lod.max_height > 4096)
509
+ continue; // skip 8k textures on mobile devices (for now)
508
510
  if (lod.max_height > pixelSizeOnScreen) {
509
511
  result.texture_lod = i;
510
512
  if (result.texture_lod < state.lastLodLevel_Texture) {
package/lib/utils.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { BufferGeometry, Object3D } from "three";
2
- export declare function getParam(name: string): boolean | string;
3
- export declare function resolveUrl(source: string | undefined, uri: string): string;
4
2
  /**
5
3
  * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
6
4
  * @param obj the object to get the raycast mesh from
@@ -8,8 +6,24 @@ export declare function resolveUrl(source: string | undefined, uri: string): str
8
6
  */
9
7
  export declare function getRaycastMesh(obj: Object3D): BufferGeometry<any> | null;
10
8
  /**
11
- * Set the raycast mesh for an object. The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
9
+ * Set the raycast mesh for an object.
10
+ * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
12
11
  * @param obj the object to set the raycast mesh for
13
12
  * @param geom the raycast mesh
14
13
  */
15
14
  export declare function setRaycastMesh(obj: Object3D, geom: BufferGeometry): void;
15
+ /**
16
+ * Call this method to enable raycasting with the lowpoly raycast meshes (if available) for all meshes in the scene.
17
+ * This is useful for performance optimization when the scene contains high poly meshes that are not visible to the camera.
18
+ * @example
19
+ * ```ts
20
+ * // call to enable raycasting with low poly raycast meshes
21
+ * useRaycastMeshes();
22
+ *
23
+ * // then use the raycaster as usual
24
+ * const raycaster = new Raycaster();
25
+ * raycaster.setFromCamera(mouse, camera);
26
+ * const intersects = raycaster.intersectObjects(scene.children, true);
27
+ * ```
28
+ */
29
+ export declare function useRaycastMeshes(enabled?: boolean): void;
@@ -0,0 +1,4 @@
1
+ export declare function getParam(name: string): boolean | string;
2
+ export declare function resolveUrl(source: string | undefined, uri: string): string;
3
+ /** @returns `true` if it's a phone or tablet */
4
+ export declare function isMobileDevice(): boolean;
@@ -0,0 +1,46 @@
1
+ export function getParam(name) {
2
+ const url = new URL(window.location.href);
3
+ const param = url.searchParams.get(name);
4
+ if (param == null || param === "0" || param === "false")
5
+ return false;
6
+ if (param === "")
7
+ return true;
8
+ return param;
9
+ }
10
+ export function resolveUrl(source, uri) {
11
+ if (uri === undefined) {
12
+ return uri;
13
+ }
14
+ if (uri.startsWith("./")) {
15
+ return uri;
16
+ }
17
+ if (uri.startsWith("http")) {
18
+ return uri;
19
+ }
20
+ if (source === undefined) {
21
+ return uri;
22
+ }
23
+ const pathIndex = source.lastIndexOf("/");
24
+ if (pathIndex >= 0) {
25
+ // Take the source uri as the base path
26
+ const basePath = source.substring(0, pathIndex + 1);
27
+ // make sure we don't have double slashes
28
+ while (basePath.endsWith("/") && uri.startsWith("/"))
29
+ uri = uri.substring(1);
30
+ // Append the relative uri
31
+ const newUri = basePath + uri;
32
+ // newUri = new URL(newUri, globalThis.location.href).href;
33
+ return newUri;
34
+ }
35
+ return uri;
36
+ }
37
+ let _ismobile;
38
+ /** @returns `true` if it's a phone or tablet */
39
+ export function isMobileDevice() {
40
+ if (_ismobile !== undefined)
41
+ return _ismobile;
42
+ _ismobile = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent);
43
+ if (getParam("debugprogressive"))
44
+ console.log("isMobileDevice", _ismobile);
45
+ return _ismobile;
46
+ }
package/lib/utils.js CHANGED
@@ -1,60 +1,82 @@
1
- import { BufferGeometry } from "three";
2
- export function getParam(name) {
3
- const url = new URL(window.location.href);
4
- const param = url.searchParams.get(name);
5
- if (param == null || param === "0" || param === "false")
6
- return false;
7
- if (param === "")
8
- return true;
9
- return param;
10
- }
11
- export function resolveUrl(source, uri) {
12
- if (uri === undefined) {
13
- return uri;
14
- }
15
- if (uri.startsWith("./")) {
16
- return uri;
17
- }
18
- if (uri.startsWith("http")) {
19
- return uri;
20
- }
21
- if (source === undefined) {
22
- return uri;
23
- }
24
- const pathIndex = source.lastIndexOf("/");
25
- if (pathIndex >= 0) {
26
- // Take the source uri as the base path
27
- const basePath = source.substring(0, pathIndex + 1);
28
- // make sure we don't have double slashes
29
- while (basePath.endsWith("/") && uri.startsWith("/"))
30
- uri = uri.substring(1);
31
- // Append the relative uri
32
- const newUri = basePath + uri;
33
- // newUri = new URL(newUri, globalThis.location.href).href;
34
- return newUri;
35
- }
36
- return uri;
37
- }
1
+ import { BufferGeometry, Mesh } from "three";
38
2
  /**
39
3
  * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
40
4
  * @param obj the object to get the raycast mesh from
41
5
  * @returns the raycast mesh or null if not set
42
6
  */
43
7
  export function getRaycastMesh(obj) {
44
- if (obj.userData?.["needle:raycast-mesh"] instanceof BufferGeometry) {
8
+ if (obj?.userData?.["needle:raycast-mesh"] instanceof BufferGeometry) {
45
9
  return obj.userData["needle:raycast-mesh"];
46
10
  }
47
11
  return null;
48
12
  }
49
13
  /**
50
- * Set the raycast mesh for an object. The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
14
+ * Set the raycast mesh for an object.
15
+ * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
51
16
  * @param obj the object to set the raycast mesh for
52
17
  * @param geom the raycast mesh
53
18
  */
54
19
  export function setRaycastMesh(obj, geom) {
55
20
  if (obj.type === "Mesh" || obj.type === "SkinnedMesh") {
56
- if (!obj.userData)
57
- obj.userData = {};
58
- obj.userData["needle:raycast-mesh"] = geom;
21
+ const existing = getRaycastMesh(obj);
22
+ if (!existing) {
23
+ const clone = shallowCloneGeometry(geom);
24
+ // remove LODs userdata to not update the geometry if the raycast mesh is rendered in the scene
25
+ clone.userData = { isRaycastMesh: true };
26
+ if (!obj.userData)
27
+ obj.userData = {};
28
+ obj.userData["needle:raycast-mesh"] = clone;
29
+ }
30
+ }
31
+ }
32
+ /**
33
+ * Call this method to enable raycasting with the lowpoly raycast meshes (if available) for all meshes in the scene.
34
+ * This is useful for performance optimization when the scene contains high poly meshes that are not visible to the camera.
35
+ * @example
36
+ * ```ts
37
+ * // call to enable raycasting with low poly raycast meshes
38
+ * useRaycastMeshes();
39
+ *
40
+ * // then use the raycaster as usual
41
+ * const raycaster = new Raycaster();
42
+ * raycaster.setFromCamera(mouse, camera);
43
+ * const intersects = raycaster.intersectObjects(scene.children, true);
44
+ * ```
45
+ */
46
+ export function useRaycastMeshes(enabled = true) {
47
+ if (enabled) {
48
+ // if the method is already patched we don't need to do it again
49
+ if (_originalRaycastMethod)
50
+ return;
51
+ const originalMethod = _originalRaycastMethod = Mesh.prototype.raycast;
52
+ Mesh.prototype.raycast = function (raycaster, intersects) {
53
+ const self = this;
54
+ const raycastMesh = getRaycastMesh(self);
55
+ let prevGeomtry;
56
+ if (raycastMesh && self.isMesh) {
57
+ prevGeomtry = self.geometry;
58
+ self.geometry = raycastMesh;
59
+ }
60
+ originalMethod.call(this, raycaster, intersects);
61
+ if (prevGeomtry) {
62
+ self.geometry = prevGeomtry;
63
+ }
64
+ };
65
+ }
66
+ else {
67
+ if (!_originalRaycastMethod)
68
+ return;
69
+ Mesh.prototype.raycast = _originalRaycastMethod;
70
+ _originalRaycastMethod = null;
71
+ }
72
+ }
73
+ let _originalRaycastMethod = null;
74
+ /** Creates a clone without copying the data */
75
+ function shallowCloneGeometry(geom) {
76
+ const clone = new BufferGeometry();
77
+ for (const key in geom.attributes) {
78
+ clone.setAttribute(key, geom.getAttribute(key));
59
79
  }
80
+ clone.setIndex(geom.getIndex());
81
+ return clone;
60
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-progressive",
3
- "version": "1.2.0-alpha.2",
3
+ "version": "1.2.0-alpha.4",
4
4
  "description": "three.js support for loading glTF or GLB files that contain progressive loading data",
5
5
  "homepage": "https://needle.tools",
6
6
  "author": {