@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in
9 lines (8 loc) • 28.7 kB
JavaScript
import{BufferGeometry as ae,Mesh as K,Texture as ee,TextureLoader as Ue,Vector3 as $,Matrix4 as be,Sphere as Fe,Box3 as Se,MeshStandardMaterial as ze,Clock as Ve}from"./three.min.js";import{DRACOLoader as qe,KTX2Loader as Xe,GLTFLoader as fe,MeshoptDecoder as Ke}from"./three-examples.min.js";const He="";globalThis.GLTF_PROGRESSIVE_VERSION=He,console.debug("[gltf-progressive] version -");let G="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",J="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ye=G,Je=J,Te=new URL(G+"draco_decoder.js");Te.searchParams.append("range","true"),fetch(Te,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${G} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),G===Ye&&Ee("./include/draco/"),J===Je&&Ae("./include/ktx2/")}).finally(()=>{Pe()});function Ee(t){G=t,A&&A[me]!=G?(console.debug("Updating Draco decoder path to "+t),A[me]=G,A.setDecoderPath(G),A.preload()):console.debug("Setting Draco decoder path to "+t)}function Ae(t){J=t,C&&C.transcoderPath!=J?(console.debug("Updating KTX2 transcoder path to "+t),C.setTranscoderPath(J),C.init()):console.debug("Setting KTX2 transcoder path to "+t)}const me=Symbol("dracoDecoderPath");let A,le,C;function Pe(){A||(A=new qe,A[me]=G,A.setDecoderPath(G),A.setDecoderConfig({type:"js"}),A.preload()),C||(C=new Xe,C.setTranscoderPath(J),C.init()),le||(le=Ke)}function pe(t){return Pe(),t?C.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:C,meshoptDecoder:le}}function ve(t){t.dracoLoader||t.setDRACOLoader(A),t.ktx2Loader||t.setKTX2Loader(C),t.meshoptDecoder||t.setMeshoptDecoder(le)}const xe=new WeakMap;function ye(t,e){let r=xe.get(t);r?r=Object.assign(r,e):r=e,xe.set(t,r)}const Be=fe.prototype.load;function Qe(...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,Be?.call(this,...t)}fe.prototype.load=Qe,te("debugprogressive");function te(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 Ze(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 ue;function et(){return ue!==void 0||(ue=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),te("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ue)),ue}const tt=typeof window>"u"&&typeof document>"u",Le=Symbol("needle:raycast-mesh");function Q(t){return t?.[Le]instanceof ae?t[Le]:null}function rt(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!Q(t)){const r=ot(e);r.userData={isRaycastMesh:!0},t[Le]=r}}function st(t=!0){if(t){if(re)return;const e=re=K.prototype.raycast;K.prototype.raycast=function(r,n){const s=this,o=Q(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),e.call(this,r,n),i&&(s.geometry=i)}}else{if(!re)return;K.prototype.raycast=re,re=null}}let re=null;function ot(t){const e=new ae;for(const r in t.attributes)e.setAttribute(r,t.getAttribute(r));return e.setIndex(t.getIndex()),e}const F=new Array,N="NEEDLE_progressive",v=te("debugprogressive"),De=Symbol("needle-progressive-texture"),se=new Map,Me=new Set;if(v){let t=function(){e+=1,console.log("Toggle LOD level",e,se),se.forEach((s,o)=>{for(const i of s.keys){const l=o[i];if(l!=null)if(l.isBufferGeometry===!0){const u=S.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,Me&&Me.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=n)}))})}function Ie(t,e,r){var n;if(!v)return;se.has(t)||se.set(t,{keys:[],sourceId:r});const s=se.get(t);((n=s?.keys)==null?void 0:n.includes(e))==!1&&s.keys.push(e)}const M=class{constructor(t,e){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[N];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>{var l;return this._isLoadingMesh=!1,i&&M.registerMesh(this.url,o.guid,i,(l=o.lods)==null?void 0:l.length,void 0,o),i})):null},v&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return N}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(v==="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 h=0;h<a.lods.length;h++){const g=a.lods[h];g.width&&(l.lods[h]=l.lods[h]||{min_height:1/0,max_height:0},l.lods[h].min_height=Math.min(l.lods[h].min_height,g.height),l.lods[h].max_height=Math.max(l.lods[h].max_height,g.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 K||t.isMesh===!0){const n=t.geometry,s=this.getAssignedLODInformation(n);if(!s)return Promise.resolve(null);for(const o of F)(r=o.onBeforeGetLODMesh)==null||r.call(o,t,e);return t["LOD:requested level"]=e,M.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,v&&Ie(t,"geometry",s.url)):v&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),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.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(v&&Me.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 ee||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(s=>{if(Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t){if(r&&n){const o=r[n];if(o&&!v){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return v==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,o,s),null}r[n]=s}if(v&&n&&r){const o=this.getAssignedLODInformation(t);o?Ie(r,n,o.url):console.warn("No LOD info for texture",t)}}return s}else v=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return v&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,s)=>{var o;if(n!=null&&n.extensions){const i=n?.extensions[N];if(i){if(!i.lods){v&&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,M.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&&M.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[N];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&&M.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=v=="verbose",l=t.userData.LODS;if(!l)return null;const u=l?.key;let a;if(t.isTexture===!0){const h=t;h.source&&h.source[De]&&(a=h.source[De])}if(a||(a=M.lodInfos.get(u)),a){if(e>0){let p=!1;const E=Array.isArray(a.lods);if(E&&e>=a.lods.length?p=!0:E||(p=!0),p)return this.lowresCache.get(u)}const h=Array.isArray(a.lods)?(r=a.lods[e])==null?void 0:r.path:a.lods;if(!h)return v&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const g=Ze(l.url,h);if(g.endsWith(".glb")||g.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const p=g+"_"+a.guid,E=this.previouslyLoaded.get(p);if(E!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${p}`);let c=await E.catch(_=>(console.error(`Error loading LOD ${e} from ${g}
`,_),null)),w=!1;if(c==null||(c instanceof ee&&t instanceof ee?(n=c.image)!=null&&n.data||(s=c.source)!=null&&s.data?c=this.copySettings(t,c):(w=!0,this.previouslyLoaded.delete(p)):c instanceof ae&&t instanceof ae&&((o=c.attributes.position)!=null&&o.array||(w=!0,this.previouslyLoaded.delete(p)))),!w)return c}const D=a,x=new Promise(async(c,w)=>{const _=new fe;ve(_),v&&(await new Promise(m=>setTimeout(m,1e3)),i&&console.warn("Start loading (delayed) "+g,D.guid));let B=g;if(D&&Array.isArray(D.lods)){const m=D.lods[e];m.hash&&(B+="?v="+m.hash)}const O=await _.loadAsync(B).catch(m=>(console.error(`Error loading LOD ${e} from ${g}
`,m),null));if(!O)return null;const I=O.parser;i&&console.log("Loading finished "+g,D.guid);let f=0;if(O.parser.json.textures){let m=!1;for(const d of O.parser.json.textures){if(d!=null&&d.extensions){const L=d?.extensions[N];if(L!=null&&L.guid&&L.guid===D.guid){m=!0;break}}f++}if(m){let d=await I.getDependency("texture",f);return d&&M.assignLODInformation(l.url,d,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',g,f,d,p),t instanceof ee&&(d=this.copySettings(t,d)),d&&(d.guid=D.guid),c(d)}else v&&console.warn("Could not find texture with guid",D.guid,O.parser.json)}if(f=0,O.parser.json.meshes){let m=!1;for(const d of O.parser.json.meshes){if(d!=null&&d.extensions){const L=d?.extensions[N];if(L!=null&&L.guid&&L.guid===D.guid){m=!0;break}}f++}if(m){const d=await I.getDependency("mesh",f),L=D;if(i&&console.log(`Loaded Mesh "${d.name}"`,g,f,d,p),d.isMesh===!0){const b=d.geometry;return M.assignLODInformation(l.url,b,u,e,void 0,L.density),c(b)}else{const b=new Array;for(let j=0;j<d.children.length;j++){const X=d.children[j];if(X.isMesh===!0){const Y=X.geometry;M.assignLODInformation(l.url,Y,u,e,j,L.density),b.push(Y)}}return c(b)}}else v&&console.warn("Could not find mesh with guid",D.guid,O.parser.json)}return c(null)});return this.previouslyLoaded.set(p,x),await x}else if(t instanceof ee){i&&console.log("Load texture from uri: "+g);const p=await new Ue().loadAsync(g);return p?(p.guid=a.guid,p.flipY=!1,p.needsUpdate=!0,p.colorSpace=t.colorSpace,i&&console.log(a,p)):v&&console.warn("failed loading",g),p}}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,s,o){if(!e)return;e.userData||(e.userData={});const i=new nt(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?(v&&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 S=M;S.registerTexture=(t,e,r,n,s)=>{if(v&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,s),!e){v&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[De]=s);const o=s.guid;M.assignLODInformation(t,e,o,r,n,void 0),M.lodInfos.set(o,s),M.lowresCache.set(o,e)},S.registerMesh=(t,e,r,n,s,o)=>{var i;v&&console.log("> Progressive: register mesh",s,r.name,o,r.uuid,r);const l=r.geometry;if(!l){v&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),M.assignLODInformation(t,l,e,n,s,o.density),M.lodInfos.set(e,o);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),n>0&&!Q(r)&&rt(r,l);for(const a of F)(i=a.onRegisteredNewMesh)==null||i.call(a,r,o)},S.lodInfos=new Map,S.previouslyLoaded=new Map,S.lowresCache=new Map;class nt{constructor(e,r,n,s,o){this.url=e,this.key=r,this.level=n,s!=null&&(this.index=s),o!=null&&(this.density=o)}}var Ce=(t,e,r)=>{if(!e.has(t))throw TypeError("Cannot "+r)},y=(t,e,r)=>(Ce(t,e,"read from private field"),r?r.call(t):e.get(t)),H=(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)},U=(t,e,r,n)=>(Ce(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),T,z,we,Z,oe,ce,V;const k=te("debugprogressive"),it=te("noprogressive"),_e=Symbol("Needle:LODSManager"),Oe=Symbol("Needle:LODState"),q=Symbol("Needle:CurrentLOD"),R={mesh_lod:-1,texture_lod:-1},P=class{constructor(t,e){this.projectionScreenMatrix=new be,this.targetTriangleDensity=2e5,this.updateInterval="auto",H(this,T,1),this.pause=!1,this.manual=!1,this._lodchangedlisteners=[],H(this,z,void 0),H(this,we,new Ve),H(this,Z,0),H(this,oe,0),H(this,ce,0),H(this,V,0),this._fpsBuffer=[60,60,60,60,60],this._sphere=new Fe,this._tempBox=new Se,this._tempBox2=new Se,this.tempMatrix=new be,this._tempWorldPosition=new $,this._tempBoxSize=new $,this._tempBox2Size=new $,this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Oe]}static addPlugin(t){F.push(t)}static removePlugin(t){const e=F.indexOf(t);e>=0&&F.splice(e,1)}static get(t,e){if(t[_e])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[_e];const r=new P(t,{engine:"unknown",...e});return t[_e]=r,r}get plugins(){return F}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(y(this,z))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;U(this,z,this.renderer.render);const e=this;pe(this.renderer),this.renderer.render=function(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,U(e,Z,y(e,Z)+1),U(e,oe,y(e,we).getDelta()),U(e,ce,y(e,ce)+y(e,oe)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/y(e,oe)),U(e,V,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),k&&y(e,Z)%200===0&&console.log("FPS",Math.round(y(e,V)),"Interval:",y(e,T)));const o=t++;y(e,z).call(this,r,n),e.onAfterRender(r,n,o)}}disable(){y(this,z)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=y(this,z),U(this,z,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(it||(this.updateInterval==="auto"?y(this,V)<40&&y(this,T)<10?(U(this,T,y(this,T)+1),k&&console.warn("\u2193 Reducing LOD updates",y(this,T),y(this,V).toFixed(0))):y(this,V)>=60&&y(this,T)>1&&(U(this,T,y(this,T)-1),k&&console.warn("\u2191 Increasing LOD updates",y(this,T),y(this,V).toFixed(0))):U(this,T,this.updateInterval),y(this,T)>0&&y(this,Z)%y(this,T)!=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")){k&&(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(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const g=Math.random()*16777215,p=new ze({color:g});a.object.material=p}const h=a.object;(h instanceof K||h.isMesh)&&this.updateLODs(t,e,h,i)}const l=s.transparent;for(const a of l){const h=a.object;(h instanceof K||h.isMesh)&&this.updateLODs(t,e,h,i)}const u=s.transmissive;for(const a of u){const h=a.object;(h instanceof K||h.isMesh)&&this.updateLODs(t,e,h,i)}}updateLODs(t,e,r,n){var s,o;r.userData||(r.userData={});let i=r[Oe];if(i||(i=new at,r[Oe]=i),i.frames++<2)return;for(const u of F)(s=u.onBeforeUpdateLOD)==null||s.call(u,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,n,R),R.mesh_lod=Math.round(R.mesh_lod),R.texture_lod=Math.round(R.texture_lod),R.mesh_lod>=0&&this.loadProgressiveMeshes(r,R.mesh_lod);let l=R.texture_lod;r.material&&l>=0&&this.loadProgressiveTextures(r.material,l);for(const u of F)(o=u.onAfterUpdatedLOD)==null||o.call(u,this.renderer,t,e,r,R);i.lastLodLevel_Mesh=R.mesh_lod,i.lastLodLevel_Texture=R.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[q]===void 0||e<t[q])&&(r=!0);const n=t["DEBUG:LOD"];n!=null&&(r=t[q]!=n,e=n),r&&(t[q]=e,S.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[q]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[q]!=n,e=n),r){t[q]=e;const s=t.geometry;return S.assignMeshLOD(t,e).then(o=>(o&&t[q]==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(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=S.getMeshLODInformation(e.geometry),a=u?.lods,h=a&&a.length>0,g=S.getMaterialMinMaxLODsCount(e.material),p=g?.min_count!=1/0&&g.min_count>0&&g.max_count>0;if(!h&&!p){s.mesh_lod=0,s.texture_lod=0;return}h||(l=!0,i=0);const E=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 c=Q(x),w=x.geometry;c&&(x.geometry=c),x.computeBoundingBox(),x.geometry=w}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 f=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),x.isPerspectiveCamera&&P.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 f=this._tempBox.min,m=this._tempBox.max;let d=f.x,L=f.y,b=m.x,j=m.y;const X=2,Y=1.5,ne=(f.x+m.x)*.5,ie=(f.y+m.y)*.5;d=(d-ne)*X+ne,L=(L-ie)*X+ie,b=(b-ne)*X+ne,j=(j-ie)*X+ie;const $e=d<0&&b>0?0:Math.min(Math.abs(f.x),Math.abs(m.x)),Ne=L<0&&j>0?0:Math.min(Math.abs(f.y),Math.abs(m.y)),ge=Math.max($e,Ne);r.lastCentrality=(Y-ge)*(Y-ge)*(Y-ge)}else r.lastCentrality=1;const c=this._tempBox.getSize(this._tempBoxSize);c.multiplyScalar(.5),screen.availHeight>0&&E>0&&c.multiplyScalar(E/screen.availHeight),t.isPerspectiveCamera?c.x*=t.aspect:t.isOrthographicCamera;const w=t.matrixWorldInverse,_=this._tempBox2;_.copy(D),_.applyMatrix4(e.matrixWorld),_.applyMatrix4(w);const B=_.getSize(this._tempBox2Size),O=Math.max(B.x,B.y);if(Math.max(c.x,c.y)!=0&&O!=0&&(c.z=B.z/Math.max(B.x,B.y)*Math.max(c.x,c.y)),r.lastScreenCoverage=Math.max(c.x,c.y,c.z),r.lastScreenspaceVolume.copy(c),r.lastScreenCoverage*=r.lastCentrality,k&&P.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const m=P.corner0,d=P.corner1,L=P.corner2,b=P.corner3;m.copy(this._tempBox.min),d.copy(this._tempBox.max),d.x=m.x,L.copy(this._tempBox.max),L.y=m.y,b.copy(this._tempBox.max);const j=(m.z+b.z)*.5;m.z=d.z=L.z=b.z=j,m.applyMatrix4(f),d.applyMatrix4(f),L.applyMatrix4(f),b.applyMatrix4(f),P.debugDrawLine(m,d,255),P.debugDrawLine(m,L,255),P.debugDrawLine(d,b,255),P.debugDrawLine(L,b,255)}let I=999;if(a&&r.lastScreenCoverage>0){for(let f=0;f<a.length;f++)if(a[f].density/r.lastScreenCoverage<n){I=f;break}}I<i&&(i=I,l=!0)}if(l?s.mesh_lod=i:s.mesh_lod=r.lastLodLevel_Mesh,k&&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(p){const x="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=g.max_count-1,k){const c=g.lods[g.max_count-1];k&&console.log(`First Texture LOD ${s.texture_lod} (${c.max_height}px) - ${e.name}`)}}else{const c=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let w=r.lastScreenCoverage*4;((o=this.context)==null?void 0:o.engine)==="model-viewer"&&(w*=1.5);const _=E/window.devicePixelRatio*w;let B=!1;for(let O=g.lods.length-1;O>=0;O--){let I=g.lods[O];if(!(x&&I.max_height>=2048)&&!(et()&&I.max_height>4096)&&(I.max_height>_||!B&&O===0)){if(B=!0,s.texture_lod=O,s.texture_lod<r.lastLodLevel_Texture){const f=I.max_height;k&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${f}px
Screensize: ${_.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${c.toFixed(1)}
${e.name}`)}break}}}}else s.texture_lod=0}};let W=P;T=new WeakMap,z=new WeakMap,we=new WeakMap,Z=new WeakMap,oe=new WeakMap,ce=new WeakMap,V=new WeakMap,W.corner0=new $,W.corner1=new $,W.corner2=new $,W.corner3=new $,W._tempPtInside=new $;class at{constructor(){this.frames=0,this.lastLodLevel_Mesh=-1,this.lastLodLevel_Texture=-1,this.lastScreenCoverage=0,this.lastScreenspaceVolume=new $,this.lastCentrality=0}}const ke=Symbol("NEEDLE_mesh_lod"),de=Symbol("NEEDLE_texture_lod");let he=null;function Re(){const t=lt();t&&(t.mapURLs(function(e){return je(),e}),je(),he?.disconnect(),he=new MutationObserver(e=>{e.forEach(r=>{r.addedNodes.forEach(n=>{n instanceof HTMLElement&&n.tagName.toLowerCase()==="model-viewer"&&We(n)})})}),he.observe(document,{childList:!0,subtree:!0}))}function lt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Re()}),null)}function je(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(t=>{We(t)})}const Ge=new WeakSet;let ut=0;function We(t){if(!t||Ge.has(t))return null;Ge.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++ut+`
`,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=W.get(e,{engine:"model-viewer"});return W.addPlugin(new ct),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 ct{constructor(){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[de]==!0)return;r[de]=!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,h,g;if(u[de]==!0)return;u[de]=!0,u.userData&&(u.userData.LOD=-1);const p=Object.keys(u);for(let E=0;E<p.length;E++){const D=p[E],x=u[D];if(x?.isTexture===!0){const c=(h=(a=x.userData)==null?void 0:a.associations)==null?void 0:h.textures;if(c==null)continue;const w=n.parser.json.textures[c];if(!w){console.warn("Texture data not found for texture index "+c);continue}if((g=w?.extensions)!=null&&g[N]){const _=w.extensions[N];_&&o&&S.registerTexture(o,x,_.lods.length,c,_)}}}};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[ke]==!0)return;r[ke]=!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[N];if(l&&i){const u=r.uuid;S.registerMesh(i,u,r,0,l.lods.length,l)}}}function dt(t,e,r,n){pe(e),ve(r),ye(r,{progressive:!0,...n?.hints}),r.register(o=>new S(o,t));const s=W.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}if(Re(),!tt){const t={gltfProgressive:{useNeedleProgressive:dt,LODsManager:W,configureLoader:ye,getRaycastMesh:Q,useRaycastMeshes:st}};if(!globalThis.Needle)globalThis.Needle=t;else for(const e in t)globalThis.Needle[e]=t[e]}export{W as LODsManager,S as NEEDLE_progressive,ve as addDracoAndKTX2Loaders,ye as configureLoader,pe as createLoaders,Q as getRaycastMesh,Ee as setDracoDecoderLocation,Ae as setKTX2TranscoderLocation};