@needle-tools/gltf-progressive
Version:
three.js support for loading glTF or GLB files that contain progressive loading data
9 lines (8 loc) • 29.5 kB
JavaScript
var Xe=Object.defineProperty,He=(t,e,r)=>e in t?Xe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(He(t,typeof e!="symbol"?e+"":e,r),r),Te=(t,e,r)=>{if(!e.has(t))throw TypeError("Cannot "+r)},L=(t,e,r)=>(Te(t,e,"read from private field"),r?r.call(t):e.get(t)),K=(t,e,r)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,r)},F=(t,e,r,n)=>(Te(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r);import{BufferGeometry as le,Mesh as Y,Texture as te,TextureLoader as Ke,Matrix4 as Ee,Clock as Ye,MeshStandardMaterial as Je,Sphere as Qe,Box3 as Ae,Vector3 as U}from"three";import{GLTFLoader as me}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ze}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as et}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as tt}from"three/examples/jsm/loaders/KTX2Loader.js";const Pe="";globalThis.GLTF_PROGRESSIVE_VERSION=Pe,console.debug("[gltf-progressive] version -");let W="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",Q="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const rt=W,st=Q,Ie=new URL(W+"draco_decoder.js");Ie.searchParams.append("range","true"),fetch(Ie,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${W} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),W===rt&&ke("./include/draco/"),Q===st&&Be("./include/ktx2/")}).finally(()=>{Ce()});function ke(t){W=t,P&&P[pe]!=W?(console.debug("Updating Draco decoder path to "+t),P[pe]=W,P.setDecoderPath(W),P.preload()):console.debug("Setting Draco decoder path to "+t)}function Be(t){Q=t,C&&C.transcoderPath!=Q?(console.debug("Updating KTX2 transcoder path to "+t),C.setTranscoderPath(Q),C.init()):console.debug("Setting KTX2 transcoder path to "+t)}const pe=Symbol("dracoDecoderPath");let P,ue,C;function Ce(){P||(P=new et,P[pe]=W,P.setDecoderPath(W),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new tt,C.setTranscoderPath(Q),C.init()),ue||(ue=Ze)}function ve(t){return Ce(),t?C.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:ue}}function ye(t){t.dracoLoader||t.setDRACOLoader(P),t.ktx2Loader||t.setKTX2Loader(C),t.meshoptDecoder||t.setMeshoptDecoder(ue)}const xe=new WeakMap;function Le(t,e){let r=xe.get(t);r?r=Object.assign(r,e):r=e,xe.set(t,r)}const Re=me.prototype.load;function ot(...t){const e=xe.get(this);let r=t[0];const n=new URL(r,window.location.href);if(n.hostname.endsWith("needle.tools")){const s=e?.progressive!==void 0?e.progressive:!0,o=e!=null&&e.usecase?e.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,r=n.toString()}return t[0]=r,Re?.call(this,...t)}me.prototype.load=ot,re("debugprogressive");function re(t){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function nt(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 ce;function it(){return ce!==void 0||(ce=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),re("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ce)),ce}const at=typeof window>"u"&&typeof document>"u",Me=Symbol("needle:raycast-mesh");function Z(t){return t?.[Me]instanceof le?t[Me]:null}function je(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!Z(t)){const r=lt(e);r.userData={isRaycastMesh:!0},t[Me]=r}}function Ne(t=!0){if(t){if(se)return;const e=se=Y.prototype.raycast;Y.prototype.raycast=function(r,n){const s=this,o=Z(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),e.call(this,r,n),i&&(s.geometry=i)}}else{if(!se)return;Y.prototype.raycast=se,se=null}}let se=null;function lt(t){const e=new le;for(const r in t.attributes)e.setAttribute(r,t.getAttribute(r));return e.setIndex(t.getIndex()),e}const z=new Array,$="NEEDLE_progressive",y=re("debugprogressive"),De=Symbol("needle-progressive-texture"),oe=new Map,we=new Set;if(y){let t=function(){e+=1,console.log("Toggle LOD level",e,oe),oe.forEach((s,o)=>{for(const i of s.keys){const l=o[i];if(l!=null)if(l.isBufferGeometry===!0){const u=T.getMeshLODInformation(l),a=u?Math.min(e,u.lods.length):0;o["DEBUG:LOD"]=a,u&&(r=Math.max(r,u.lods.length-1))}else o.isMaterial===!0&&(o["DEBUG:LOD"]=e)}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(n=!n,we&&we.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=n)}))})}function Ge(t,e,r){var n;if(!y)return;oe.has(t)||oe.set(t,{keys:[],sourceId:r});const s=oe.get(t);((n=s?.keys)==null?void 0:n.includes(e))==!1&&s.keys.push(e)}const w=class{constructor(t,e){c(this,"parser"),c(this,"url"),c(this,"_isLoadingMesh"),c(this,"loadMesh",r=>{var n,s;if(this._isLoadingMesh)return null;const o=(s=(n=this.parser.json.meshes[r])==null?void 0:n.extensions)==null?void 0:s[$];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>{var l;return this._isLoadingMesh=!1,i&&w.registerMesh(this.url,o.guid,i,(l=o.lods)==null?void 0:l.length,void 0,o),i})):null}),y&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return $}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",s=t[n];if(s!=null)return s;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(y==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const l of Object.keys(i.uniforms)){const u=i.uniforms[l].value;u?.isTexture===!0&&o(u,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const l=t[i];l?.isTexture===!0&&o(l,e)}return t[n]=e,e;function o(i,l){const u=r.getAssignedLODInformation(i);if(u){const a=r.lodInfos.get(u.key);if(a&&a.lods){l.min_count=Math.min(l.min_count,a.lods.length),l.max_count=Math.max(l.max_count,a.lods.length);for(let g=0;g<a.lods.length;g++){const f=a.lods[g];f.width&&(l.lods[g]=l.lods[g]||{min_height:1/0,max_height:0},l.lods[g].min_height=Math.min(l.lods[g].min_height,f.height),l.lods[g].max_height=Math.max(l.lods[g].max_height,f.height))}}}}}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let n,s;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(r=n?.userData)!=null&&r.LODS){const o=n.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof Y||t.isMesh===!0){const n=t.geometry,s=this.getAssignedLODInformation(n);if(!s)return Promise.resolve(null);for(const o of z)(r=o.onBeforeGetLODMesh)==null||r.call(o,t,e);return t["LOD:requested level"]=e,w.getOrLoadLOD(n,e).then(o=>{if(Array.isArray(o)){const i=s.index||0;o=o[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&n!=o&&(o?.isBufferGeometry?(t.geometry=o,y&&Ge(t,"geometry",s.url)):y&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else y&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const r=t;if(Array.isArray(r.material)){const n=new Array;for(const s of r.material){const o=this.assignTextureLOD(s,e);n.push(o)}return Promise.all(n).then(s=>{const o=new Array;for(const i of s)Array.isArray(i)&&o.push(...i);return o})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,n=[],s=new Array;if(y&&we.add(r),r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const o=r;for(const i of Object.keys(o.uniforms)){const l=o.uniforms[i].value;if(l?.isTexture===!0){const u=this.assignTextureLODForSlot(l,e,r,i).then(a=>(a&&o.uniforms[i].value!=a&&(o.uniforms[i].value=a,o.uniformsNeedUpdate=!0),a));n.push(u),s.push(i)}}}else for(const o of Object.keys(r)){const i=r[o];if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,r,o);n.push(l),s.push(o)}}return Promise.all(n).then(o=>{const i=new Array;for(let l=0;l<o.length;l++){const u=o[l],a=s[l];u&&u.isTexture===!0?i.push({material:r,slot:a,texture:u,level:e}):i.push({material:r,slot:a,texture:null,level:e})}return i})}if(t instanceof te||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):w.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t){if(r&&n){const o=r[n];if(o&&!y){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return y==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,o,s),null}r[n]=s}if(y&&n&&r){const o=this.getAssignedLODInformation(t);o?Ge(r,n,o.url):console.warn("No LOD info for texture",t)}}return s}else y=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return y&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,s)=>{var o;if(n!=null&&n.extensions){const i=n?.extensions[$];if(i){if(!i.lods){y&&console.warn("Texture has no LODs",i);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const a=this.parser.associations.get(u);a?.textures===s&&(l=!0,w.registerTexture(this.url,u,(o=i.lods)==null?void 0:o.length,s,i))}l||this.parser.getDependency("texture",s).then(u=>{var a;u&&w.registerTexture(this.url,u,(a=i.lods)==null?void 0:a.length,s,i)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,s)=>{if(n!=null&&n.extensions){const o=n?.extensions[$];if(o&&o.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const l=this.parser.associations.get(i);l?.meshes===s&&w.registerMesh(this.url,o.guid,i,o.lods.length,l.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var r,n,s,o;const i=y=="verbose",l=t.userData.LODS;if(!l)return null;const u=l?.key;let a;if(t.isTexture===!0){const g=t;g.source&&g.source[De]&&(a=g.source[De])}if(a||(a=w.lodInfos.get(u)),a){if(e>0){let v=!1;const A=Array.isArray(a.lods);if(A&&e>=a.lods.length?v=!0:A||(v=!0),v)return this.lowresCache.get(u)}const g=Array.isArray(a.lods)?(r=a.lods[e])==null?void 0:r.path:a.lods;if(!g)return y&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const f=nt(l.url,g);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const v=f+"_"+a.guid,A=this.previouslyLoaded.get(v);if(A!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${v}`);let d=await A.catch(O=>(console.error(`Error loading LOD ${e} from ${f}
`,O),null)),b=!1;if(d==null||(d instanceof te&&t instanceof te?(n=d.image)!=null&&n.data||(s=d.source)!=null&&s.data?d=this.copySettings(t,d):(b=!0,this.previouslyLoaded.delete(v)):d instanceof le&&t instanceof le&&((o=d.attributes.position)!=null&&o.array||(b=!0,this.previouslyLoaded.delete(v)))),!b)return d}const D=a,x=new Promise(async(d,b)=>{const O=new me;ye(O),y&&(await new Promise(p=>setTimeout(p,1e3)),i&&console.warn("Start loading (delayed) "+f,D.guid));let k=f;if(D&&Array.isArray(D.lods)){const p=D.lods[e];p.hash&&(k+="?v="+p.hash)}const _=await O.loadAsync(k).catch(p=>(console.error(`Error loading LOD ${e} from ${f}
`,p),null));if(!_)return null;const B=_.parser;i&&console.log("Loading finished "+f,D.guid);let m=0;if(_.parser.json.textures){let p=!1;for(const h of _.parser.json.textures){if(h!=null&&h.extensions){const M=h?.extensions[$];if(M!=null&&M.guid&&M.guid===D.guid){p=!0;break}}m++}if(p){let h=await B.getDependency("texture",m);return h&&w.assignLODInformation(l.url,h,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+h.name+'"',f,m,h,v),t instanceof te&&(h=this.copySettings(t,h)),h&&(h.guid=D.guid),d(h)}else y&&console.warn("Could not find texture with guid",D.guid,_.parser.json)}if(m=0,_.parser.json.meshes){let p=!1;for(const h of _.parser.json.meshes){if(h!=null&&h.extensions){const M=h?.extensions[$];if(M!=null&&M.guid&&M.guid===D.guid){p=!0;break}}m++}if(p){const h=await B.getDependency("mesh",m),M=D;if(i&&console.log(`Loaded Mesh "${h.name}"`,f,m,h,v),h.isMesh===!0){const S=h.geometry;return w.assignLODInformation(l.url,S,u,e,void 0,M.density),d(S)}else{const S=new Array;for(let G=0;G<h.children.length;G++){const H=h.children[G];if(H.isMesh===!0){const J=H.geometry;w.assignLODInformation(l.url,J,u,e,G,M.density),S.push(J)}}return d(S)}}else y&&console.warn("Could not find mesh with guid",D.guid,_.parser.json)}return d(null)});return this.previouslyLoaded.set(v,x),await x}else if(t instanceof te){i&&console.log("Load texture from uri: "+f);const v=await new Ke().loadAsync(f);return v?(v.guid=a.guid,v.flipY=!1,v.needsUpdate=!0,v.colorSpace=t.colorSpace,i&&console.log(a,v)):y&&console.warn("failed loading",f),v}}else y&&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,s,o){if(!e)return;e.userData||(e.userData={});const i=new ut(t,r,n,s,o);e.userData.LODS=i}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e?(y&&console.warn(`Copy texture settings
`,t.uuid,`
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}};let T=w;c(T,"registerTexture",(t,e,r,n,s)=>{if(y&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,s),!e){y&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[De]=s);const o=s.guid;w.assignLODInformation(t,e,o,r,n,void 0),w.lodInfos.set(o,s),w.lowresCache.set(o,e)}),c(T,"registerMesh",(t,e,r,n,s,o)=>{var i;y&&console.log("> Progressive: register mesh",s,r.name,o,r.uuid,r);const l=r.geometry;if(!l){y&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),w.assignLODInformation(t,l,e,n,s,o.density),w.lodInfos.set(e,o);let u=w.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],w.lowresCache.set(e,u),n>0&&!Z(r)&&je(r,l);for(const a of z)(i=a.onRegisteredNewMesh)==null||i.call(a,r,o)}),c(T,"lodInfos",new Map),c(T,"previouslyLoaded",new Map),c(T,"lowresCache",new Map);class ut{constructor(e,r,n,s,o){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=n,s!=null&&(this.index=s),o!=null&&(this.density=o)}}const R=re("debugprogressive"),ct=re("noprogressive"),be=Symbol("Needle:LODSManager"),Oe=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),j={mesh_lod:-1,texture_lod:-1};var E,q,_e,ee,ne,de,X;const I=class{constructor(t,e){c(this,"context"),c(this,"renderer"),c(this,"projectionScreenMatrix",new Ee),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval","auto"),K(this,E,1),c(this,"pause",!1),c(this,"manual",!1),c(this,"_lodchangedlisteners",[]),K(this,q,void 0),K(this,_e,new Ye),K(this,ee,0),K(this,ne,0),K(this,de,0),K(this,X,0),c(this,"_fpsBuffer",[60,60,60,60,60]),c(this,"_sphere",new Qe),c(this,"_tempBox",new Ae),c(this,"_tempBox2",new Ae),c(this,"tempMatrix",new Ee),c(this,"_tempWorldPosition",new U),c(this,"_tempBoxSize",new U),c(this,"_tempBox2Size",new U),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Oe]}static addPlugin(t){z.push(t)}static removePlugin(t){const e=z.indexOf(t);e>=0&&z.splice(e,1)}static get(t,e){if(t[be])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[be];const r=new I(t,{engine:"unknown",...e});return t[be]=r,r}get plugins(){return z}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}enable(){if(L(this,q))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;F(this,q,this.renderer.render);const e=this;ve(this.renderer),this.renderer.render=function(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,F(e,ee,L(e,ee)+1),F(e,ne,L(e,_e).getDelta()),F(e,de,L(e,de)+L(e,ne)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/L(e,ne)),F(e,X,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),R&&L(e,ee)%200===0&&console.log("FPS",Math.round(L(e,X)),"Interval:",L(e,E)));const o=t++;L(e,q).call(this,r,n),e.onAfterRender(r,n,o)}}disable(){L(this,q)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=L(this,q),F(this,q,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const n=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(n.length===1){const o=n[0].material;(o.name==="EffectMaterial"||o.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ct||(this.updateInterval==="auto"?L(this,X)<40&&L(this,E)<10?(F(this,E,L(this,E)+1),R&&console.warn("\u2193 Reducing LOD updates",L(this,E),L(this,X).toFixed(0))):L(this,X)>=60&&L(this,E)>1&&(F(this,E,L(this,E)-1),R&&console.warn("\u2191 Increasing LOD updates",L(this,E),L(this,X).toFixed(0))):F(this,E,this.updateInterval),L(this,E)>0&&L(this,ee)%L(this,E)!=0))return;this.internalUpdate(t,e)}}internalUpdate(t,e){var r,n;const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const i=this.targetTriangleDensity;for(const a of o){if(a.material&&(((r=a.geometry)==null?void 0:r.type)==="BoxGeometry"||((n=a.geometry)==null?void 0:n.type)==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){R&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(R==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,v=new Je({color:f});a.object.material=v}const g=a.object;(g instanceof Y||g.isMesh)&&this.updateLODs(t,e,g,i)}const l=s.transparent;for(const a of l){const g=a.object;(g instanceof Y||g.isMesh)&&this.updateLODs(t,e,g,i)}const u=s.transmissive;for(const a of u){const g=a.object;(g instanceof Y||g.isMesh)&&this.updateLODs(t,e,g,i)}}updateLODs(t,e,r,n){var s,o;r.userData||(r.userData={});let i=r[Oe];if(i||(i=new dt,r[Oe]=i),i.frames++<2)return;for(const u of z)(s=u.onBeforeUpdateLOD)==null||s.call(u,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,n,j),j.mesh_lod=Math.round(j.mesh_lod),j.texture_lod=Math.round(j.texture_lod),j.mesh_lod>=0&&this.loadProgressiveMeshes(r,j.mesh_lod);let l=j.texture_lod;r.material&&l>=0&&this.loadProgressiveTextures(r.material,l);for(const u of z)(o=u.onAfterUpdatedLOD)==null||o.call(u,this.renderer,t,e,r,j);i.lastLodLevel_Mesh=j.mesh_lod,i.lastLodLevel_Texture=j.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let r=!1;(t[V]===void 0||e<t[V])&&(r=!0);const n=t["DEBUG:LOD"];n!=null&&(r=t[V]!=n,e=n),r&&(t[V]=e,T.assignTextureLOD(t,e).then(s=>{this._lodchangedlisteners.forEach(o=>o({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[V]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[V]!=n,e=n),r){t[V]=e;const s=t.geometry;return T.assignMeshLOD(t,e).then(o=>(o&&t[V]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(i=>i({type:"mesh",level:e,object:t})),o))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,o=(r.y+n.y)*.5;return this._tempPtInside.set(s,o,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,n,s){var o;if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let i=10+1,l=!1;if(R&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=T.getMeshLODInformation(e.geometry),a=u?.lods,g=a&&a.length>0,f=T.getMaterialMinMaxLODsCount(e.material),v=f?.min_count!=1/0&&f.min_count>0&&f.max_count>0;if(!g&&!v){s.mesh_lod=0,s.texture_lod=0;return}g||(l=!0,i=0);const A=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const x=e;if(!x.boundingBox)x.computeBoundingBox();else if(r.frames%30===0){const d=Z(x),b=x.geometry;d&&(x.geometry=d),x.computeBoundingBox(),x.geometry=b}D=x.boundingBox}if(D){const x=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 m=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(m)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),x.isPerspectiveCamera&&I.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&x.isPerspectiveCamera&&x.fov>70){const m=this._tempBox.min,p=this._tempBox.max;let h=m.x,M=m.y,S=p.x,G=p.y;const H=2,J=1.5,ie=(m.x+p.x)*.5,ae=(m.y+p.y)*.5;h=(h-ie)*H+ie,M=(M-ae)*H+ae,S=(S-ie)*H+ie,G=(G-ae)*H+ae;const Ve=h<0&&S>0?0:Math.min(Math.abs(m.x),Math.abs(p.x)),qe=M<0&&G>0?0:Math.min(Math.abs(m.y),Math.abs(p.y)),fe=Math.max(Ve,qe);r.lastCentrality=(J-fe)*(J-fe)*(J-fe)}else r.lastCentrality=1;const d=this._tempBox.getSize(this._tempBoxSize);d.multiplyScalar(.5),screen.availHeight>0&&A>0&&d.multiplyScalar(A/screen.availHeight),t.isPerspectiveCamera?d.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,O=this._tempBox2;O.copy(D),O.applyMatrix4(e.matrixWorld),O.applyMatrix4(b);const k=O.getSize(this._tempBox2Size),_=Math.max(k.x,k.y);if(Math.max(d.x,d.y)!=0&&_!=0&&(d.z=k.z/Math.max(k.x,k.y)*Math.max(d.x,d.y)),r.lastScreenCoverage=Math.max(d.x,d.y,d.z),r.lastScreenspaceVolume.copy(d),r.lastScreenCoverage*=r.lastCentrality,R&&I.debugDrawLine){const m=this.tempMatrix.copy(this.projectionScreenMatrix);m.invert();const p=I.corner0,h=I.corner1,M=I.corner2,S=I.corner3;p.copy(this._tempBox.min),h.copy(this._tempBox.max),h.x=p.x,M.copy(this._tempBox.max),M.y=p.y,S.copy(this._tempBox.max);const G=(p.z+S.z)*.5;p.z=h.z=M.z=S.z=G,p.applyMatrix4(m),h.applyMatrix4(m),M.applyMatrix4(m),S.applyMatrix4(m),I.debugDrawLine(p,h,255),I.debugDrawLine(p,M,255),I.debugDrawLine(h,S,255),I.debugDrawLine(M,S,255)}let B=999;if(a&&r.lastScreenCoverage>0){for(let m=0;m<a.length;m++)if(a[m].density/r.lastScreenCoverage<n){B=m;break}}B<i&&(i=B,l=!0)}if(l?s.mesh_lod=i:s.mesh_lod=r.lastLodLevel_Mesh,R&&s.mesh_lod!=r.lastLodLevel_Mesh){const x=a?.[s.mesh_lod];x&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (${x.density.toFixed(0)}) - ${e.name}`)}if(v){const x="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=f.max_count-1,R){const d=f.lods[f.max_count-1];R&&console.log(`First Texture LOD ${s.texture_lod} (${d.max_height}px) - ${e.name}`)}}else{const d=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let b=r.lastScreenCoverage*4;((o=this.context)==null?void 0:o.engine)==="model-viewer"&&(b*=1.5);const O=A/window.devicePixelRatio*b;let k=!1;for(let _=f.lods.length-1;_>=0;_--){let B=f.lods[_];if(!(x&&B.max_height>=2048)&&!(it()&&B.max_height>4096)&&(B.max_height>O||!k&&_===0)){if(k=!0,s.texture_lod=_,s.texture_lod<r.lastLodLevel_Texture){const m=B.max_height;R&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${m}px
Screensize: ${O.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${d.toFixed(1)}
${e.name}`)}break}}}}else s.texture_lod=0}};let N=I;E=new WeakMap,q=new WeakMap,_e=new WeakMap,ee=new WeakMap,ne=new WeakMap,de=new WeakMap,X=new WeakMap,c(N,"debugDrawLine"),c(N,"corner0",new U),c(N,"corner1",new U),c(N,"corner2",new U),c(N,"corner3",new U),c(N,"_tempPtInside",new U);class dt{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",-1),c(this,"lastLodLevel_Texture",-1),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new U),c(this,"lastCentrality",0)}}const We=Symbol("NEEDLE_mesh_lod"),he=Symbol("NEEDLE_texture_lod");let ge=null;function Se(){const t=ht();t&&(t.mapURLs(function(e){return $e(),e}),$e(),ge?.disconnect(),ge=new MutationObserver(e=>{e.forEach(r=>{r.addedNodes.forEach(n=>{n instanceof HTMLElement&&n.tagName.toLowerCase()==="model-viewer"&&Ue(n)})})}),ge.observe(document,{childList:!0,subtree:!0}))}function ht(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Se()}),null)}function $e(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(t=>{Ue(t)})}const Fe=new WeakSet;let gt=0;function Ue(t){if(!t||Fe.has(t))return null;Fe.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++gt+`
`,t.getAttribute("src"));let e=null,r=null,n=null;for(let s=t;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(a=>a.toString()=="Symbol(renderer)"),l=o.find(a=>a.toString()=="Symbol(scene)"),u=o.find(a=>a.toString()=="Symbol(needsRender)");!e&&i!=null&&(e=t[i].threeRenderer),!r&&l!=null&&(r=t[l]),!n&&u!=null&&(n=t[u])}if(e&&r){let s=function(){if(n){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}n?.call(t)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=N.get(e,{engine:"model-viewer"});return N.addPlugin(new ft),o.enable(),o.addEventListener("changed",()=>{n?.call(t)}),t.addEventListener("model-visibility",i=>{i.detail.visible&&n?.call(t)}),t.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class ft{constructor(){c(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,r,n,s){this.tryParseMeshLOD(r,s),this.tryParseTextureLOD(r,s)}getUrl(e){if(!e)return null;let r=e.getAttribute("src");return r||(r=e.src),r||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),r}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,r){if(r[he]==!0)return;r[he]=!0;const n=this.tryGetCurrentGLTF(e),s=this.tryGetCurrentModelViewer(e),o=this.getUrl(s);if(o&&n&&r.material){let i=function(u){var a,g,f;if(u[he]==!0)return;u[he]=!0,u.userData&&(u.userData.LOD=-1);const v=Object.keys(u);for(let A=0;A<v.length;A++){const D=v[A],x=u[D];if(x?.isTexture===!0){const d=(g=(a=x.userData)==null?void 0:a.associations)==null?void 0:g.textures;if(d==null)continue;const b=n.parser.json.textures[d];if(!b){console.warn("Texture data not found for texture index "+d);continue}if((f=b?.extensions)!=null&&f[$]){const O=b.extensions[$];O&&o&&T.registerTexture(o,x,O.lods.length,d,O)}}}};const l=r.material;if(Array.isArray(l))for(const u of l)i(u);else i(l)}}tryParseMeshLOD(e,r){var n,s;if(r[We]==!0)return;r[We]=!0;const o=this.tryGetCurrentModelViewer(e),i=this.getUrl(o);if(!i)return;const l=(s=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[$];if(l&&i){const u=r.uuid;T.registerMesh(i,u,r,0,l.lods.length,l)}}}function ze(t,e,r,n){ve(e),ye(r),Le(r,{progressive:!0,...n?.hints}),r.register(o=>new T(o,t));const s=N.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}if(Se(),!at){const t={gltfProgressive:{useNeedleProgressive:ze,LODsManager:N,configureLoader:Le,getRaycastMesh:Z,useRaycastMeshes:Ne}};if(!globalThis.Needle)globalThis.Needle=t;else for(const e in t)globalThis.Needle[e]=t[e]}export{$ as EXTENSION_NAME,N as LODsManager,T as NEEDLE_progressive,Pe as VERSION,ye as addDracoAndKTX2Loaders,Le as configureLoader,ve as createLoaders,Z as getRaycastMesh,Se as patchModelViewer,je as registerRaycastMesh,ke as setDracoDecoderLocation,Be as setKTX2TranscoderLocation,ze as useNeedleProgressive,Ne as useRaycastMeshes};