@needle-tools/gltf-progressive 1.0.0-alpha.8 → 1.1.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
- var se=Object.defineProperty,oe=(r,e,t)=>e in r?se(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,c=(r,e,t)=>(oe(r,typeof e!="symbol"?e+"":e,t),t);import{Mesh as U,BufferGeometry as F,Material as ne,RawShaderMaterial as ie,Texture as k,TextureLoader as ae,Matrix4 as Y,Frustum as le,Sphere as ue,Box3 as J,Vector3 as B}from"three";import{GLTFLoader as ce}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as de}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as he}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as fe}from"three/examples/jsm/loaders/KTX2Loader.js";const ge="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",me="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";let R,$,G;function Q(r){R||(R=new he,R.setDecoderPath(ge),R.setDecoderConfig({type:"js"})),G||(G=new fe,G.setTranscoderPath(me)),$||($=de),r?G.detectSupport(r):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Z(r){r.dracoLoader||r.setDRACOLoader(R),r.ktx2Loader||r.setKTX2Loader(G),r.meshoptDecoder||r.setMeshoptDecoder($)}function q(r){const e=new URL(window.location.href).searchParams.get(r);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function pe(r,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||r===void 0)return e;const t=r.lastIndexOf("/");if(t>=0){const o=r.substring(0,t+1);for(;o.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return o+e}return e}const V=new Array;function ye(r){V.push(r)}const P="NEEDLE_progressive",L=q("debugprogressive"),X=Symbol("needle-progressive-texture"),N=new Map,H=new Set;if(L){let r=function(){e+=1,console.log("Toggle LOD level",e,N),N.forEach((n,s)=>{for(const i of n.keys){const a=s[i];if(a.isBufferGeometry===!0){const u=O.getMeshLODInformation(a),h=u?Math.min(e,u.lods.length):0;s["DEBUG:LOD"]=e,O.assignMeshLOD(s,h),u&&(t=Math.max(t,u.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,O.assignTextureLOD(s,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,o=!1;window.addEventListener("keyup",n=>{n.key==="p"&&r(),n.key==="w"&&(o=!o,H&&H.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=o)}))})}function ee(r,e,t){var o;if(!L)return;N.has(r)||N.set(r,{keys:[],sourceId:t});const n=N.get(r);((o=n?.keys)==null?void 0:o.includes(e))==!1&&n.keys.push(e)}const D=class{constructor(r,e){c(this,"parser"),c(this,"url"),L&&console.log("Progressive extension registered for",e),this.parser=r,this.url=e}get name(){return P}static getMeshLODInformation(r){const e=this.getAssignedLODInformation(r);return e!=null&&e.key?this.lodInfos.get(e.key):null}static hasLODLevelAvailable(r,e){var t;if(r.isMaterial===!0){for(const s of Object.keys(r)){const i=r[s];if(i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(r.isGroup===!0){for(const s of r.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let o,n;if(r.isMesh?o=r.geometry:(r.isBufferGeometry||r.isTexture)&&(o=r),o&&(t=o?.userData)!=null&&t.LODS){const s=o.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(r,e){var t;if(!r)return Promise.resolve(null);if(r instanceof U||r.isMesh===!0){const o=r.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of V)(t=s.onBeforeGetLODMesh)==null||t.call(s,r,e);return r["LOD:requested level"]=e,D.getOrLoadLOD(o,e).then(s=>{if(r["LOD:requested level"]===e){if(delete r["LOD:requested level"],Array.isArray(s)){const i=n.index||0;s=s[i]}s&&o!=s&&s instanceof F&&(r.geometry=s,L&&ee(r,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",r,s),null))}else L&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",r);return Promise.resolve(null)}static assignTextureLOD(r,e=0){if(!r)return Promise.resolve(null);if(r instanceof ne||r.isMaterial===!0){const t=r,o=[],n=new Array;if(L&&H.add(t),t instanceof ie)for(const s of Object.keys(t.uniforms)){const i=t.uniforms[s].value;if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);o.push(a),n.push(s)}}else for(const s of Object.keys(t)){const i=t[s];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);o.push(a),n.push(s)}}return Promise.all(o).then(s=>{const i=new Array;for(let a=0;a<s.length;a++){const u=s[a],h=n[a];u&&u.isTexture===!0?i.push({material:t,slot:h,texture:u,level:e}):i.push({material:t,slot:h,texture:null,level:e})}return i})}if(r instanceof k||r.isTexture===!0){const t=r;return this.assignTextureLODForSlot(t,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(r,e,t,o){return r?.isTexture!==!0?Promise.resolve(null):D.getOrLoadLOD(r,e).then(n=>{if(Array.isArray(n))return null;if(n?.isTexture===!0){if(n!=r&&(t&&o&&(t[o]=n),L&&o&&t)){const s=this.getAssignedLODInformation(r);s&&ee(t,o,s.url)}return n}else L=="verbose"&&console.warn("No LOD found for",r,e);return null}).catch(n=>(console.error("Error loading LOD",r,n),null))}afterRoot(r){var e,t;return L&&console.log("AFTER",this.url,r),(e=this.parser.json.textures)==null||e.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[P];if(s){let i=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===n&&(i=!0,D.registerTexture(this.url,a,n,s));i||this.parser.getDependency("texture",n).then(a=>{a&&D.registerTexture(this.url,a,n,s)})}}}),(t=this.parser.json.meshes)==null||t.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.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===n&&D.registerMesh(this.url,s.guid,i,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,o,n;const s=L=="verbose",i=r.userData.LODS;if(!i)return null;const a=i?.key;let u;if(r.isTexture===!0){const h=r;h.source&&h.source[X]&&(u=h.source[X])}if(u||(u=D.lodInfos.get(a)),u){if(e>0){let d=!1;const M=Array.isArray(u.lods);if(M&&e>=u.lods.length?d=!0:M||(d=!0),d)return this.lowresCache.get(a)}const h=Array.isArray(u.lods)?u.lods[e].path:u.lods;if(!h)return L&&!u["missing:uri"]&&(u["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,u)),null;const l=pe(i.url,h);if(l.endsWith(".glb")||l.endsWith(".gltf")){if(!u.guid)return console.warn("missing pointer for glb/gltf texture",u),null;const d=l+"_"+u.guid,M=this.previouslyLoaded.get(d);if(M!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${d}`);let p=await M.catch(m=>(console.error(`Error loading LOD ${e} from ${l}
2
- `,m),null)),g=!1;if(p==null||(p instanceof k&&r instanceof k?(t=p.image)!=null&&t.data||(o=p.source)!=null&&o.data?p=this.copySettings(r,p):(g=!0,this.previouslyLoaded.delete(d)):p instanceof F&&r instanceof F&&((n=p.attributes.position)!=null&&n.array||(g=!0,this.previouslyLoaded.delete(d)))),!g)return p}const y=u,A=new Promise(async(p,g)=>{const m=new ce;Z(m),L&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+l,y.guid));let b=l;if(y&&Array.isArray(y.lods)){const v=y.lods[e];v.hash&&(b+="?v="+v.hash)}const x=await m.loadAsync(b).catch(v=>(console.error(`Error loading LOD ${e} from ${l}
3
- `,v),null));if(!x)return null;const S=x.parser;s&&console.log("Loading finished "+l,y.guid);let _=0;if(x.parser.json.textures){let v=!1;for(const f of x.parser.json.textures){if(f!=null&&f.extensions){const w=f?.extensions[P];if(w!=null&&w.guid&&w.guid===y.guid){v=!0;break}}_++}if(v){let f=await S.getDependency("texture",_);return s&&console.log('change "'+r.name+'" \u2192 "'+f.name+'"',l,_,f,d),r instanceof k&&(f=this.copySettings(r,f)),f&&(f.guid=y.guid),p(f)}}if(_=0,x.parser.json.meshes){let v=!1;for(const f of x.parser.json.meshes){if(f!=null&&f.extensions){const w=f?.extensions[P];if(w!=null&&w.guid&&w.guid===y.guid){v=!0;break}}_++}if(v){const f=await S.getDependency("mesh",_),w=y;if(s&&console.log(`Loaded Mesh "${f.name}"`,l,_,f,d),f.isMesh===!0){const E=f.geometry;return D.assignLODInformation(i.url,E,a,e,void 0,w.density),p(E)}else{const E=new Array;for(let C=0;C<f.children.length;C++){const W=f.children[C];if(W instanceof U){const j=W.geometry;D.assignLODInformation(i.url,j,a,e,C,w.density),E.push(j)}}return p(E)}}}return p(null)});return this.previouslyLoaded.set(d,A),await A}else if(r instanceof k){s&&console.log("Load texture from uri: "+l);const d=await new ae().loadAsync(l);return d?(d.guid=u.guid,d.flipY=!1,d.needsUpdate=!0,d.colorSpace=r.colorSpace,s&&console.log(u,d)):L&&console.warn("failed loading",l),d}}else L&&console.warn(`Can not load LOD ${e}: no LOD info found for "${a}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,o,n,s){if(!e)return;e.userData||(e.userData={});const i=new xe(r,t,o,n,s);e.userData.LODS=i,e.userData.LOD=o}static getAssignedLODInformation(r){var e;return((e=r?.userData)==null?void 0:e.LODS)||null}static copySettings(r,e){return this._copiedTextures.get(r)||(e=e.clone(),this._copiedTextures.set(r,e),e.offset=r.offset,e.repeat=r.repeat,e.colorSpace=r.colorSpace,e)}};let O=D;c(O,"registerTexture",(r,e,t,o)=>{L&&console.log("> Progressive: register texture",t,e.name,e.uuid,e,o),e.source&&(e.source[X]=o);const n=o.guid;D.assignLODInformation(r,e,n,0,0,void 0),D.lodInfos.set(n,o),D.lowresCache.set(n,e)}),c(O,"registerMesh",(r,e,t,o,n,s)=>{var i;L&&console.log("> Progressive: register mesh",n,t.name,s,t.uuid,t);const a=t.geometry;a.userData||(a.userData={}),D.assignLODInformation(r,a,e,o,n,s.density),D.lodInfos.set(e,s);let u=D.lowresCache.get(e);u?u.push(t.geometry):u=[t.geometry],D.lowresCache.set(e,u);for(const h of V)(i=h.onRegisteredNewMesh)==null||i.call(h,t,s)}),c(O,"lodInfos",new Map),c(O,"previouslyLoaded",new Map),c(O,"lowresCache",new Map),c(O,"_copiedTextures",new Map);class xe{constructor(e,t,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=t,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}const K=q("debugprogressive"),ve=q("noprogressive"),T=class{constructor(r){c(this,"renderer"),c(this,"projectionScreenMatrix",new Y),c(this,"cameraFrustrum",new le),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"plugins",[]),c(this,"_originalRender"),c(this,"_sphere",new ue),c(this,"_tempBox",new J),c(this,"tempMatrix",new Y),c(this,"_tempWorldPosition",new B),c(this,"_tempBoxSize",new B),c(this,"_tempBox2Size",new B),this.renderer=r}static getObjectLODState(r){var e;return(e=r.userData)==null?void 0:e.LOD_state}enable(){if(this._originalRender)return;let r=0;this._originalRender=this.renderer.render;const e=this;let t=0;Q(this.renderer),this.renderer.render=function(o,n){const s=t++,i=r++;e.onBeforeRender(o,n,i,s),e._originalRender.call(this,o,n),e.onAfterRender(o,n,i,s),r--}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(r,e,t,o){}onAfterRender(r,e,t,o){var n,s;if(ve||this.pause||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const i=1e5,a=this.renderer.renderLists.get(r,t),u=a.opaque;for(const l of u){if(l.material&&(((n=l.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=l.geometry)==null?void 0:s.type)==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){K&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}const d=l.object;(d instanceof U||d.isMesh)&&this.updateLODs(r,e,d,i)}const h=a.transparent;for(const l of h){const d=l.object;(d instanceof U||d.isMesh)&&this.updateLODs(r,e,d,i)}}updateLODs(r,e,t,o){var n,s;for(const h of this.plugins)(n=h.onBeforeUpdateLOD)==null||n.call(h,this.renderer,r,e,t);let i=t.userData.LOD_state;i||(i=new Le,t.userData.LOD_state=i);let a=this.calculateLodLevel(e,t,i,o);a=Math.round(a),a>=0&&this.loadProgressiveMeshes(t,a);let u=0;if(t.material){const h=t["DEBUG:LOD"];if(h!=null&&(u=h),Array.isArray(t.material))for(const l of t.material)this.loadProgressiveTextures(l,u);else this.loadProgressiveTextures(t.material,u)}for(const h of this.plugins)(s=h.onAfterUpdatedLOD)==null||s.call(h,this.renderer,r,e,t,a);i.lastLodLevel=a}loadProgressiveTextures(r,e){return r&&r.userData&&r.userData.LOD!==e?(r.userData.LOD=e,O.assignTextureLOD(r,e)):Promise.resolve(null)}loadProgressiveMeshes(r,e){if(!r)return Promise.resolve(null);if(r.userData||(r.userData={}),r.userData.LOD!==e){r.userData.LOD=e;const t=r.geometry;return O.assignMeshLOD(r,e).then(o=>(o&&r.userData.LOD==e&&t!=r.geometry,o))}return Promise.resolve(null)}calculateLodLevel(r,e,t,o){var n;if(!e)return-1;let s=10+1;if(r){if(K&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=O.getMeshLODInformation(e.geometry),a=i?.lods;if(!a||a.length<=0||!((n=this.cameraFrustrum)!=null&&n.intersectsObject(e)))return 99;const u=e.geometry.boundingBox;if(u&&r.isPerspectiveCamera){const h=r;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=r.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g))return 0}if(this._tempBox.copy(u),this._tempBox.applyMatrix4(e.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&h.fov>70){const g=this._tempBox.min,m=this._tempBox.max;let b=g.x,x=g.y,S=m.x,_=m.y;const v=2,f=1.5,w=(g.x+m.x)*.5,E=(g.y+m.y)*.5;b=(b-w)*v+w,x=(x-E)*v+E,S=(S-w)*v+w,_=(_-E)*v+E;const C=b<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(m.x)),W=x<0&&_>0?0:Math.min(Math.abs(g.y),Math.abs(m.y)),j=Math.max(C,W);t.lastCentrality=(f-j)*(f-j)*(f-j)}else t.lastCentrality=1;const l=this._tempBox.getSize(this._tempBoxSize);l.multiplyScalar(.5),screen.availHeight>0&&l.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),l.x*=h.aspect;const d=r.matrixWorldInverse,M=new J;M.copy(u),M.applyMatrix4(e.matrixWorld),M.applyMatrix4(d);const y=M.getSize(this._tempBox2Size),A=Math.max(y.x,y.y);if(Math.max(l.x,l.y)!=0&&A!=0&&(l.z=y.z/Math.max(y.x,y.y)*Math.max(l.x,l.y)),t.lastScreenCoverage=Math.max(l.x,l.y,l.z),t.lastScreenspaceVolume.copy(l),t.lastScreenCoverage*=t.lastCentrality,K&&T.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const m=T.corner0,b=T.corner1,x=T.corner2,S=T.corner3;m.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=m.x,x.copy(this._tempBox.max),x.y=m.y,S.copy(this._tempBox.max);const _=(m.z+S.z)*.5;m.z=b.z=x.z=S.z=_,m.applyMatrix4(g),b.applyMatrix4(g),x.applyMatrix4(g),S.applyMatrix4(g),T.debugDrawLine(m,b,255),T.debugDrawLine(m,x,255),T.debugDrawLine(b,S,255),T.debugDrawLine(x,S,255)}let p=999;if(a&&t.lastScreenCoverage>0){for(let g=0;g<a.length;g++)if(a[g].density/t.lastScreenCoverage<o){p=g;break}}p<s&&(s=p)}}return s}};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);class Le{constructor(){c(this,"lastLodLevel",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new B),c(this,"lastCentrality",0)}}const re=Symbol("NEEDLE_mesh_lod"),z=Symbol("NEEDLE_texture_lod");function te(r){if(!r)return null;let e=null,t=null;for(let o=r;o!=null;o=Object.getPrototypeOf(o)){const n=Object.getOwnPropertySymbols(o),s=n.find(a=>a.toString()=="Symbol(renderer)"),i=n.find(a=>a.toString()=="Symbol(scene)");!e&&s!=null&&(e=r[s].threeRenderer),!t&&i!=null&&(t=r[i])}if(e){console.log("Adding Needle LODs to modelviewer");const o=new I(e);if(o.plugins.push(new De(r)),o.enable(),t){const n=t.camera||t.traverse(s=>s.type=="PerspectiveCamera")[0];n&&e.render(t,n)}return()=>{o.disable()}}return null}class De{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,t,o,n){this.tryParseMeshLOD(t,n),this.tryParseTextureLOD(t,n)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[z]==!0)return;t[z]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&t.material){let s=function(a){var u,h,l;if(a[z]==!0)return;a[z]=!0,a.userData&&(a.userData.LOD=-1);const d=Object.keys(a);for(let M=0;M<d.length;M++){const y=d[M],A=a[y];if(A?.isTexture===!0){const p=(h=(u=A.userData)==null?void 0:u.associations)==null?void 0:h.textures,g=o.parser.json.textures[p];if((l=g.extensions)!=null&&l[P]){const m=g.extensions[P];m&&n&&O.registerTexture(n,A,m.lods.length,m)}}}};const i=t.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}tryParseMeshLOD(e,t){var o,n;if(t[re]==!0)return;t[re]=!0;const s=this.getUrl();if(!s)return;const i=(n=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[P];if(i&&s){const a=t.uuid;O.registerMesh(s,a,t,0,i.lods.length,i)}}}function Oe(r,e,t,o){Q(e),Z(t),t.register(s=>new O(s,r));const n=new I(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{te(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,I as LODsManager,O as NEEDLE_progressive,te as patchModelViewer,ye as registerPlugin,Oe as useNeedleProgressive};
1
+ var fe=Object.defineProperty,me=(t,e,r)=>e in t?fe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(me(t,typeof e!="symbol"?e+"":e,r),r);import{MeshoptDecoder as ge}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as pe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as ye}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as $,Mesh as q,Material as xe,Texture as F,TextureLoader as Le,Matrix4 as se,Frustum as De,Sphere as ve,Box3 as oe,Vector3 as P}from"three";import{GLTFLoader as Me}from"three/examples/jsm/loaders/GLTFLoader.js";let V="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",H="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(V+"draco_decoder.js",{method:"head"}).catch(t=>{V="./include/draco/",H="./include/ktx2/"});function Oe(t){V=t}function we(t){H=t}let U,Y,W;function J(t){U||(U=new pe,U.setDecoderPath(V),U.setDecoderConfig({type:"js"})),W||(W=new ye,W.setTranscoderPath(H)),Y||(Y=ge),t?W.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Q(t){t.dracoLoader||t.setDRACOLoader(U),t.ktx2Loader||t.setKTX2Loader(W),t.meshoptDecoder||t.setMeshoptDecoder(Y)}function Z(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function _e(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const o=t.substring(0,r+1);for(;o.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return o+e}return e}function ne(t){var e;return((e=t.userData)==null?void 0:e["needle:raycast-mesh"])instanceof $?t.userData["needle:raycast-mesh"]:null}function ie(t,e){(t.type==="Mesh"||t.type==="SkinnedMesh")&&(t.userData||(t.userData={}),t.userData["needle:raycast-mesh"]=e)}const C=new Array,I="NEEDLE_progressive",v=Z("debugprogressive"),ee=Symbol("needle-progressive-texture"),z=new Map,te=new Set;if(v){let t=function(){e+=1,console.log("Toggle LOD level",e,z),z.forEach((n,s)=>{for(const a of n.keys){const i=s[a];if(i.isBufferGeometry===!0){const u=w.getMeshLODInformation(i),l=u?Math.min(e,u.lods.length):0;s["DEBUG:LOD"]=e,w.assignMeshLOD(s,l),u&&(r=Math.max(r,u.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.assignTextureLOD(s,e);break}}}),e>=r&&(e=-1)},e=-1,r=2,o=!1;window.addEventListener("keyup",n=>{n.key==="p"&&t(),n.key==="w"&&(o=!o,te&&te.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=o)}))})}function ae(t,e,r){var o;if(!v)return;z.has(t)||z.set(t,{keys:[],sourceId:r});const n=z.get(t);((o=n?.keys)==null?void 0:o.includes(e))==!1&&n.keys.push(e)}const M=class{constructor(t,e){c(this,"parser"),c(this,"url"),v&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return I}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e={min:1/0,max:-1}){if(Array.isArray(t)){for(const n of t)this.getMaterialMinMaxLODsCount(n,e);return e}let r=e.min,o=e.max;if(t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const n=t;for(const s of Object.keys(n.uniforms)){const a=n.uniforms[s].value;if(a?.isTexture===!0){const i=this.getAssignedLODInformation(a);if(i){const u=this.lodInfos.get(i.key);u&&u.lods&&(r=Math.min(r,u.lods.length),o=Math.max(o,u.lods.length))}}}}else if(t.isMaterial)for(const n of Object.keys(t)){const s=t[n];if(s?.isTexture===!0){const a=this.getAssignedLODInformation(s);if(a){const i=this.lodInfos.get(a.key);i&&i.lods&&(r=Math.min(r,i.lods.length),o=Math.max(o,i.lods.length))}}}return e.min=r,e.max=o,e}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const a=t[s];if(a&&a.isTexture&&this.hasLODLevelAvailable(a,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let o,n;if(t.isMesh?o=t.geometry:(t.isBufferGeometry||t.isTexture)&&(o=t),o&&(r=o?.userData)!=null&&r.LODS){const s=o.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof q||t.isMesh===!0){const o=t.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of C)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(o,e).then(s=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(s)){const a=n.index||0;s=s[a]}s&&o!=s&&s instanceof $&&(t.geometry=s,v&&ae(t,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else v&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t instanceof xe||t.isMaterial===!0){const r=t,o=[],n=new Array;if(v&&te.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const a of Object.keys(s.uniforms)){const i=s.uniforms[a].value;if(i?.isTexture===!0){const u=this.assignTextureLODForSlot(i,e,r,a);o.push(u),n.push(a)}}}else for(const s of Object.keys(r)){const a=r[s];if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,r,s);o.push(i),n.push(s)}}return Promise.all(o).then(s=>{const a=new Array;for(let i=0;i<s.length;i++){const u=s[i],l=n[i];u&&u.isTexture===!0?a.push({material:r,slot:l,texture:u,level:e}):a.push({material:r,slot:l,texture:null,level:e})}return a})}if(t instanceof F||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(o==="glyphMap")return Promise.resolve(t);const n="LOD:requested level:"+o;return r&&(r[n]=e),M.getOrLoadLOD(t,e).then(s=>{if(r&&r[n]!=e||Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t&&(r&&o&&(r[o]=s),v&&o&&r)){const a=this.getAssignedLODInformation(t);a&&ae(r,o,a.url)}return s}else v=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return v&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[I];if(s){let a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===n&&(a=!0,M.registerTexture(this.url,i,n,s));a||this.parser.getDependency("texture",n).then(i=>{i&&M.registerTexture(this.url,i,n,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[I];if(s&&s.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===n&&M.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var r,o,n,s;const a=v=="verbose",i=t.userData.LODS;if(!i)return null;const u=i?.key;let l;if(t.isTexture===!0){const x=t;x.source&&x.source[ee]&&(l=x.source[ee])}if(l||(l=M.lodInfos.get(u)),l){if(e>0){let f=!1;const b=Array.isArray(l.lods);if(b&&e>=l.lods.length?f=!0:b||(f=!0),f)return this.lowresCache.get(u)}const x=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!x)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=_e(i.url,x);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=h+"_"+l.guid,b=this.previouslyLoaded.get(f);if(b!==void 0){a&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let p=await b.catch(G=>(console.error(`Error loading LOD ${e} from ${h}
2
+ `,G),null)),_=!1;if(p==null||(p instanceof F&&t instanceof F?(o=p.image)!=null&&o.data||(n=p.source)!=null&&n.data?p=this.copySettings(t,p):(_=!0,this.previouslyLoaded.delete(f)):p instanceof $&&t instanceof $&&((s=p.attributes.position)!=null&&s.array||(_=!0,this.previouslyLoaded.delete(f)))),!_)return p}const m=l,k=new Promise(async(p,_)=>{const G=new Me;Q(G),v&&(await new Promise(y=>setTimeout(y,1e3)),a&&console.warn("Start loading (delayed) "+h,m.guid));let N=h;if(m&&Array.isArray(m.lods)){const y=m.lods[e];y.hash&&(N+="?v="+y.hash)}const g=await G.loadAsync(N).catch(y=>(console.error(`Error loading LOD ${e} from ${h}
3
+ `,y),null));if(!g)return null;const L=g.parser;a&&console.log("Loading finished "+h,m.guid);let D=0;if(g.parser.json.textures){let y=!1;for(const d of g.parser.json.textures){if(d!=null&&d.extensions){const O=d?.extensions[I];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){let d=await L.getDependency("texture",D);return d&&M.assignLODInformation(i.url,d,u,e,void 0,void 0),a&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',h,D,d,f),t instanceof F&&(d=this.copySettings(t,d)),d&&(d.guid=m.guid),p(d)}else v&&console.warn("Could not find texture with guid",m.guid)}if(D=0,g.parser.json.meshes){let y=!1;for(const d of g.parser.json.meshes){if(d!=null&&d.extensions){const O=d?.extensions[I];if(O!=null&&O.guid&&O.guid===m.guid){y=!0;break}}D++}if(y){const d=await L.getDependency("mesh",D),O=m;if(a&&console.log(`Loaded Mesh "${d.name}"`,h,D,d,f),d.isMesh===!0){const T=d.geometry;return M.assignLODInformation(i.url,T,u,e,void 0,O.density),p(T)}else{const T=new Array;for(let B=0;B<d.children.length;B++){const j=d.children[B];if(j instanceof q){const R=j.geometry;M.assignLODInformation(i.url,R,u,e,B,O.density),T.push(R)}}return p(T)}}}return p(null)});return this.previouslyLoaded.set(f,k),await k}else if(t instanceof F){a&&console.log("Load texture from uri: "+h);const f=await new Le().loadAsync(h);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,a&&console.log(l,f)):v&&console.warn("failed loading",h),f}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,o,n,s){if(!e)return;e.userData||(e.userData={});const a=new be(t,r,o,n,s);e.userData.LODS=a,e.userData.LOD=o}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),v&&console.warn(`Copying texture settings
4
+ `,t.uuid,`
5
+ `,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let w=M;c(w,"registerTexture",(t,e,r,o)=>{v&&console.log("> Progressive: register texture",r,e.name,e.uuid,e,o),e.source&&(e.source[ee]=o);const n=o.guid;M.assignLODInformation(t,e,n,0,0,void 0),M.lodInfos.set(n,o),M.lowresCache.set(n,e)}),c(w,"registerMesh",(t,e,r,o,n,s)=>{var a;v&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const i=r.geometry;i.userData||(i.userData={}),M.assignLODInformation(t,i,e,o,n,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),o>0&&!ne(r)&&ie(r,i);for(const l of C)(a=l.onRegisteredNewMesh)==null||a.call(l,r,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class be{constructor(e,r,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}const re=Z("debugprogressive"),Se=Z("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},S=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new De),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new ve),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new P),c(this,"_tempBoxSize",new P),c(this,"_tempBox2Size",new P),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){C.push(t)}static removePlugin(t){const e=C.indexOf(t);e>=0&&C.splice(e,1)}static get(t){return t[le]?t[le]:new S(t)}get plugins(){return C}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;J(this.renderer),this.renderer.render=function(r,o){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const n=e._frame,s=t++;e.onBeforeRender(r,o,s,n),e._originalRender.call(this,r,o),e.onAfterRender(r,o,s,n)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,o){}onAfterRender(t,e,r,o){var n,s;if(this.pause)return;const a=this.renderer.renderLists.get(t,0),i=a.opaque;let u=!0;if(i.length===1){const l=i[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of i){if(h.material&&(((n=h.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){re&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}const x=a.transparent;for(const h of x){const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}}}updateLODs(t,e,r,o,n){var s,a;let i=r.userData.LOD_state;if(i||(i=new Te,r.userData.LOD_state=i),i.frames++<2)return;for(const l of C)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,o,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of C)(a=l.onAfterUpdatedLOD)==null||a.call(l,this.renderer,t,e,r,E);i.lastLodLevel_Mesh=E.mesh_lod,i.lasLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}const r=t;let o=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(o=!0),o&&(r.NEEDLE_LOD=e,w.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return w.assignMeshLOD(t,e).then(o=>(o&&t.userData.LOD==e&&r!=t.geometry,o))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,o=t.max,n=(r.x+o.x)*.5,s=(r.y+o.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,o,n){var s;if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1;if(re&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=w.getMeshLODInformation(e.geometry),u=i?.lods,l=u&&u.length>0,x=w.getMaterialMinMaxLODsCount(e.material),h=x?.min!=1/0&&x.min>0&&x.max>0;if(!l&&!h){n.mesh_lod=0,n.texture_lod=0;return}if(l||(a=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){n.mesh_lod=99,n.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const b=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),S.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&b.fov>70){const g=this._tempBox.min,L=this._tempBox.max;let D=g.x,y=g.y,d=L.x,O=L.y;const T=2,B=1.5,j=(g.x+L.x)*.5,R=(g.y+L.y)*.5;D=(D-j)*T+j,y=(y-R)*T+R,d=(d-j)*T+j,O=(O-R)*T+R;const de=D<0&&d>0?0:Math.min(Math.abs(g.x),Math.abs(L.x)),he=y<0&&O>0?0:Math.min(Math.abs(g.y),Math.abs(L.y)),K=Math.max(de,he);r.lastCentrality=(B-K)*(B-K)*(B-K)}else r.lastCentrality=1;const m=this._tempBox.getSize(this._tempBoxSize);m.multiplyScalar(.5),screen.availHeight>0&&m.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),m.x*=b.aspect;const k=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(k);const _=p.getSize(this._tempBox2Size),G=Math.max(_.x,_.y);if(Math.max(m.x,m.y)!=0&&G!=0&&(m.z=_.z/Math.max(_.x,_.y)*Math.max(m.x,m.y)),r.lastScreenCoverage=Math.max(m.x,m.y,m.z),r.lastScreenspaceVolume.copy(m),r.lastScreenCoverage*=r.lastCentrality,re&&S.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const L=S.corner0,D=S.corner1,y=S.corner2,d=S.corner3;L.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=L.x,y.copy(this._tempBox.max),y.y=L.y,d.copy(this._tempBox.max);const O=(L.z+d.z)*.5;L.z=D.z=y.z=d.z=O,L.applyMatrix4(g),D.applyMatrix4(g),y.applyMatrix4(g),d.applyMatrix4(g),S.debugDrawLine(L,D,255),S.debugDrawLine(L,y,255),S.debugDrawLine(D,d,255),S.debugDrawLine(y,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let g=0;g<u.length;g++)if(u[g].density/r.lastScreenCoverage<o){N=g;break}}N<a&&(a=N)}if(n.mesh_lod=a,x!=null&&x.min&&x.min>0&&x.max>0){const b=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=Ee(x.max,0,b)}else n.texture_lod=0}};let A=S;c(A,"debugDrawLine"),c(A,"corner0",new P),c(A,"corner1",new P),c(A,"corner2",new P),c(A,"corner3",new P),c(A,"_tempPtInside",new P);function Ee(t,e,r){return t+(e-t)*r}class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",0),c(this,"lasLodLevel_Texture",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new P),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let o=t;o!=null;o=Object.getPrototypeOf(o)){const n=Object.getOwnPropertySymbols(o),s=n.find(i=>i.toString()=="Symbol(renderer)"),a=n.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=t[s].threeRenderer),!r&&a!=null&&(r=t[a])}if(e){console.log("Adding Needle LODs to modelviewer");const o=A.get(e);if(A.addPlugin(new Ae(t)),o.enable(),r){const n=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];n&&e.render(r,n)}return()=>{o.disable()}}return null}class Ae{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,o,n){this.tryParseMeshLOD(r,n),this.tryParseTextureLOD(r,n)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[X]==!0)return;r[X]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&r.material){let s=function(i){var u,l,x;if(i[X]==!0)return;i[X]=!0,i.userData&&(i.userData.LOD=-1);const h=Object.keys(i);for(let f=0;f<h.length;f++){const b=h[f],m=i[b];if(m?.isTexture===!0){const k=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=o.parser.json.textures[k];if(!p){console.warn("Texture data not found for texture index "+k);continue}if((x=p?.extensions)!=null&&x[I]){const _=p.extensions[I];_&&n&&w.registerTexture(n,m,_.lods.length,_)}}}};const a=r.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,r){var o,n;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const a=(n=(o=r.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[I];if(a&&s){const i=r.uuid;w.registerMesh(s,i,r,0,a.lods.length,a)}}}function Pe(t,e,r,o){J(e),Q(r),r.register(s=>new w(s,t));const n=A.get(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{I as EXTENSION_NAME,A as LODsManager,w as NEEDLE_progressive,Q as addDracoAndKTX2Loaders,J as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,we as setKTX2TranscoderLocation,ie as setRaycastMesh,Pe as useNeedleProgressive};
@@ -1,3 +1,5 @@
1
- "use strict";var re=Object.defineProperty;var se=(l,e,t)=>e in l?re(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var u=(l,e,t)=>(se(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("three"),ie=require("three/examples/jsm/loaders/GLTFLoader.js"),oe=require("three/examples/jsm/libs/meshopt_decoder.module.js"),ne=require("three/examples/jsm/loaders/DRACOLoader.js"),ae=require("three/examples/jsm/loaders/KTX2Loader.js"),le="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ue="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";let F,X,U;function Z(l){F||(F=new ne.DRACOLoader,F.setDecoderPath(le),F.setDecoderConfig({type:"js"})),U||(U=new ae.KTX2Loader,U.setTranscoderPath(ue)),X||(X=oe.MeshoptDecoder),l?U.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function j(l){l.dracoLoader||l.setDRACOLoader(F),l.ktx2Loader||l.setKTX2Loader(U),l.meshoptDecoder||l.setMeshoptDecoder(X)}function H(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function ce(l,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||l===void 0)return e;const t=l.lastIndexOf("/");if(t>=0){const r=l.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}const K=new Array;function fe(l){K.push(l)}const C="NEEDLE_progressive",T=H("debugprogressive"),q=Symbol("needle-progressive-texture"),N=new Map,Y=new Set;if(T){let l=function(){e+=1,console.log("Toggle LOD level",e,N),N.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n.isBufferGeometry===!0){const a=w.getMeshLODInformation(n),p=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,w.assignMeshLOD(o,p),a&&(t=Math.max(t,a.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,w.assignTextureLOD(o,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&l(),i.key==="w"&&(r=!r,Y&&Y.forEach(o=>{o.name!="BackgroundCubeMaterial"&&"wireframe"in o&&(o.wireframe=r)}))})}function J(l,e,t){var i;if(!T)return;N.has(l)||N.set(l,{keys:[],sourceId:t});const r=N.get(l);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const S=class{constructor(e,t){u(this,"parser");u(this,"url");T&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return C}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static hasLODLevelAvailable(e,t){var o;if(e.isMaterial===!0){for(const s of Object.keys(e)){const n=e[s];if(n.isTexture&&this.hasLODLevelAvailable(n,t))return!0}return!1}else if(e.isGroup===!0){for(const s of e.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,t))return!0}let r,i;if(e.isMesh?r=e.geometry:(e.isBufferGeometry||e.isTexture)&&(r=e),r&&(o=r==null?void 0:r.userData)!=null&&o.LODS){const s=r.userData.LODS;if(i=this.lodInfos.get(s.key),t===void 0)return i!=null;if(i)return Array.isArray(i.lods)?t<i.lods.length:t===0}return!1}static assignMeshLOD(e,t){var r;if(!e)return Promise.resolve(null);if(e instanceof h.Mesh||e.isMesh===!0){const i=e.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of K)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,S.getOrLoadLOD(i,t).then(s=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(s)){const n=o.index||0;s=s[n]}s&&i!=s&&s instanceof h.BufferGeometry&&(e.geometry=s,T&&J(e,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else T&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",e);return Promise.resolve(null)}static assignTextureLOD(e,t=0){if(!e)return Promise.resolve(null);if(e instanceof h.Material||e.isMaterial===!0){const r=e,i=[],o=new Array;if(T&&Y.add(r),r instanceof h.RawShaderMaterial)for(const s of Object.keys(r.uniforms)){const n=r.uniforms[s].value;if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,s);i.push(a),o.push(s)}}else for(const s of Object.keys(r)){const n=r[s];if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,s);i.push(a),o.push(s)}}return Promise.all(i).then(s=>{const n=new Array;for(let a=0;a<s.length;a++){const p=s[a],d=o[a];p&&p.isTexture===!0?n.push({material:r,slot:d,texture:p,level:t}):n.push({material:r,slot:d,texture:null,level:t})}return n})}if(e instanceof h.Texture||e.isTexture===!0){const r=e;return this.assignTextureLODForSlot(r,t,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(e,t,r,i){return(e==null?void 0:e.isTexture)!==!0?Promise.resolve(null):S.getOrLoadLOD(e,t).then(o=>{if(Array.isArray(o))return null;if((o==null?void 0:o.isTexture)===!0){if(o!=e&&(r&&i&&(r[i]=o),T&&i&&r)){const s=this.getAssignedLODInformation(e);s&&J(r,i,s.url)}return o}else T=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(o=>(console.error("Error loading LOD",e,o),null))}afterRoot(e){var t,r;return T&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[C];if(s){let n=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===o&&(n=!0,S.registerTexture(this.url,a,o,s));n||this.parser.getDependency("texture",o).then(a=>{a&&S.registerTexture(this.url,a,o,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[C];if(s&&s.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const a=this.parser.associations.get(n);a.meshes===o&&S.registerMesh(this.url,s.guid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,p;const r=T=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const d=e;d.source&&d.source[q]&&(s=d.source[q])}if(s||(s=S.lodInfos.get(o)),s){if(t>0){let f=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?f=!0:M||(f=!0),f)return this.lowresCache.get(o)}const d=Array.isArray(s.lods)?s.lods[t].path:s.lods;if(!d)return T&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const c=ce(i.url,d);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const f=c+"_"+s.guid,M=this.previouslyLoaded.get(f);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${f}`);let x=await M.catch(I=>(console.error(`Error loading LOD ${t} from ${c}
2
- `,I),null)),R=!1;if(x==null||(x instanceof h.Texture&&e instanceof h.Texture?(n=x.image)!=null&&n.data||(a=x.source)!=null&&a.data?x=this.copySettings(e,x):(R=!0,this.previouslyLoaded.delete(f)):x instanceof h.BufferGeometry&&e instanceof h.BufferGeometry&&((p=x.attributes.position)!=null&&p.array||(R=!0,this.previouslyLoaded.delete(f)))),!R)return x}const D=s,P=new Promise(async(x,R)=>{const I=new ie.GLTFLoader;j(I),T&&(await new Promise(L=>setTimeout(L,1e3)),r&&console.warn("Start loading (delayed) "+c,D.guid));let m=c;if(D&&Array.isArray(D.lods)){const L=D.lods[t];L.hash&&(m+="?v="+L.hash)}const y=await I.loadAsync(m).catch(L=>(console.error(`Error loading LOD ${t} from ${c}
3
- `,L),null));if(!y)return null;const b=y.parser;r&&console.log("Loading finished "+c,D.guid);let O=0;if(y.parser.json.textures){let L=!1;for(const g of y.parser.json.textures){if(g!=null&&g.extensions){const v=g==null?void 0:g.extensions[C];if(v!=null&&v.guid&&v.guid===D.guid){L=!0;break}}O++}if(L){let g=await b.getDependency("texture",O);return r&&console.log('change "'+e.name+'" → "'+g.name+'"',c,O,g,f),e instanceof h.Texture&&(g=this.copySettings(e,g)),g&&(g.guid=D.guid),x(g)}}if(O=0,y.parser.json.meshes){let L=!1;for(const g of y.parser.json.meshes){if(g!=null&&g.extensions){const v=g==null?void 0:g.extensions[C];if(v!=null&&v.guid&&v.guid===D.guid){L=!0;break}}O++}if(L){const g=await b.getDependency("mesh",O),v=D;if(r&&console.log(`Loaded Mesh "${g.name}"`,c,O,g,f),g.isMesh===!0){const B=g.geometry;return S.assignLODInformation(i.url,B,o,t,void 0,v.density),x(B)}else{const B=new Array;for(let E=0;E<g.children.length;E++){const k=g.children[E];if(k instanceof h.Mesh){const W=k.geometry;S.assignLODInformation(i.url,W,o,t,E,v.density),B.push(W)}}return x(B)}}}return x(null)});return this.previouslyLoaded.set(f,P),await P}else if(e instanceof h.Texture){r&&console.log("Load texture from uri: "+c);const M=await new h.TextureLoader().loadAsync(c);return M?(M.guid=s.guid,M.flipY=!1,M.needsUpdate=!0,M.colorSpace=e.colorSpace,r&&console.log(s,M)):T&&console.warn("failed loading",c),M}}else T&&console.warn(`Can not load LOD ${t}: no LOD info found for "${o}" ${e.name}`,e.type);return null}static assignLODInformation(e,t,r,i,o,s){if(!t)return;t.userData||(t.userData={});const n=new de(e,r,i,o,s);t.userData.LODS=n,t.userData.LOD=i}static getAssignedLODInformation(e){var t;return((t=e==null?void 0:e.userData)==null?void 0:t.LODS)||null}static copySettings(e,t){const r=this._copiedTextures.get(e);return r||(t=t.clone(),this._copiedTextures.set(e,t),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t)}};let w=S;u(w,"registerTexture",(e,t,r,i)=>{T&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[q]=i);const o=i.guid;S.assignLODInformation(e,t,o,0,0,void 0),S.lodInfos.set(o,i),S.lowresCache.set(o,t)}),u(w,"registerMesh",(e,t,r,i,o,s)=>{var p;T&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;n.userData||(n.userData={}),S.assignLODInformation(e,n,t,i,o,s.density),S.lodInfos.set(t,s);let a=S.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],S.lowresCache.set(t,a);for(const d of K)(p=d.onRegisteredNewMesh)==null||p.call(d,r,s)}),u(w,"lodInfos",new Map),u(w,"previouslyLoaded",new Map),u(w,"lowresCache",new Map),u(w,"_copiedTextures",new Map);class de{constructor(e,t,r,i,o){u(this,"url");u(this,"key");u(this,"level");u(this,"index");u(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const $=H("debugprogressive"),ge=H("noprogressive"),A=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new h.Matrix4);u(this,"cameraFrustrum",new h.Frustum);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"plugins",[]);u(this,"_originalRender");u(this,"_sphere",new h.Sphere);u(this,"_tempBox",new h.Box3);u(this,"tempMatrix",new h.Matrix4);u(this,"_tempWorldPosition",new h.Vector3);u(this,"_tempBoxSize",new h.Vector3);u(this,"_tempBox2Size",new h.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;let r=0;Z(this.renderer),this.renderer.render=function(i,o){const s=r++,n=e++;t.onBeforeRender(i,o,n,s),t._originalRender.call(this,i,o),t.onAfterRender(i,o,n,s),e--}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var p,d;if(ge||this.pause||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const o=1e5,s=this.renderer.renderLists.get(e,r),n=s.opaque;for(const c of n){if(c.material&&(((p=c.geometry)==null?void 0:p.type)==="BoxGeometry"||((d=c.geometry)==null?void 0:d.type)==="BufferGeometry")&&(c.material.name==="SphericalGaussianBlur"||c.material.name=="BackgroundCubeMaterial"||c.material.name==="CubemapFromEquirect"||c.material.name==="EquirectangularToCubeUV")){$&&(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",c,c.material.name,c.material.type)));continue}const f=c.object;(f instanceof h.Mesh||f.isMesh)&&this.updateLODs(e,t,f,o)}const a=s.transparent;for(const c of a){const f=c.object;(f instanceof h.Mesh||f.isMesh)&&this.updateLODs(e,t,f,o)}}updateLODs(e,t,r,i){var a,p;for(const d of this.plugins)(a=d.onBeforeUpdateLOD)==null||a.call(d,this.renderer,e,t,r);let o=r.userData.LOD_state;o||(o=new he,r.userData.LOD_state=o);let s=this.calculateLodLevel(t,r,o,i);s=Math.round(s),s>=0&&this.loadProgressiveMeshes(r,s);let n=0;if(r.material){const d=r["DEBUG:LOD"];if(d!=null&&(n=d),Array.isArray(r.material))for(const c of r.material)this.loadProgressiveTextures(c,n);else this.loadProgressiveTextures(r.material,n)}for(const d of this.plugins)(p=d.onAfterUpdatedLOD)==null||p.call(d,this.renderer,e,t,r,s);o.lastLodLevel=s}loadProgressiveTextures(e,t){return e&&e.userData&&e.userData.LOD!==t?(e.userData.LOD=t,w.assignTextureLOD(e,t)):Promise.resolve(null)}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return w.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}calculateLodLevel(e,t,r,i){var n;if(!t)return-1;let s=10+1;if(e){if($&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const a=w.getMeshLODInformation(t.geometry),p=a==null?void 0:a.lods;if(!p||p.length<=0||!((n=this.cameraFrustrum)!=null&&n.intersectsObject(t)))return 99;const d=t.geometry.boundingBox;if(d&&e.isPerspectiveCamera){const c=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 m=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(m))return 0}if(this._tempBox.copy(d),this._tempBox.applyMatrix4(t.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.fov>70){const m=this._tempBox.min,y=this._tempBox.max;let b=m.x,O=m.y,L=y.x,g=y.y;const v=2,B=1.5,E=(m.x+y.x)*.5,k=(m.y+y.y)*.5;b=(b-E)*v+E,O=(O-k)*v+k,L=(L-E)*v+E,g=(g-k)*v+k;const W=b<0&&L>0?0:Math.min(Math.abs(m.x),Math.abs(y.x)),te=O<0&&g>0?0:Math.min(Math.abs(m.y),Math.abs(y.y)),V=Math.max(W,te);r.lastCentrality=(B-V)*(B-V)*(B-V)}else r.lastCentrality=1;const f=this._tempBox.getSize(this._tempBoxSize);f.multiplyScalar(.5),screen.availHeight>0&&f.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),f.x*=c.aspect;const M=e.matrixWorldInverse,D=new h.Box3;D.copy(d),D.applyMatrix4(t.matrixWorld),D.applyMatrix4(M);const P=D.getSize(this._tempBox2Size),G=Math.max(P.x,P.y);if(Math.max(f.x,f.y)!=0&&G!=0&&(f.z=P.z/Math.max(P.x,P.y)*Math.max(f.x,f.y)),r.lastScreenCoverage=Math.max(f.x,f.y,f.z),r.lastScreenspaceVolume.copy(f),r.lastScreenCoverage*=r.lastCentrality,$&&A.debugDrawLine){const m=this.tempMatrix.copy(this.projectionScreenMatrix);m.invert();const y=A.corner0,b=A.corner1,O=A.corner2,L=A.corner3;y.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=y.x,O.copy(this._tempBox.max),O.y=y.y,L.copy(this._tempBox.max);const g=(y.z+L.z)*.5;y.z=b.z=O.z=L.z=g,y.applyMatrix4(m),b.applyMatrix4(m),O.applyMatrix4(m),L.applyMatrix4(m),A.debugDrawLine(y,b,255),A.debugDrawLine(y,O,255),A.debugDrawLine(b,L,255),A.debugDrawLine(O,L,255)}let R=999;if(p&&r.lastScreenCoverage>0){for(let m=0;m<p.length;m++)if(p[m].density/r.lastScreenCoverage<i){R=m;break}}R<s&&(s=R)}}return s}};let _=A;u(_,"debugDrawLine"),u(_,"corner0",new h.Vector3),u(_,"corner1",new h.Vector3),u(_,"corner2",new h.Vector3),u(_,"corner3",new h.Vector3);class he{constructor(){u(this,"lastLodLevel",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new h.Vector3);u(this,"lastCentrality",0)}}const Q=Symbol("NEEDLE_mesh_lod"),z=Symbol("NEEDLE_texture_lod");function ee(l){if(!l)return null;let e=null,t=null;for(let r=l;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),o=i.find(n=>n.toString()=="Symbol(renderer)"),s=i.find(n=>n.toString()=="Symbol(scene)");!e&&o!=null&&(e=l[o].threeRenderer),!t&&s!=null&&(t=l[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=new _(e);if(r.plugins.push(new pe(l)),r.enable(),t){const i=t.camera||t.traverse(o=>o.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class pe{constructor(e){u(this,"modelviewer");u(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[z]==!0)return;t[z]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let o=function(n){var p,d,c;if(n[z]==!0)return;n[z]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let f=0;f<a.length;f++){const M=a[f],D=n[M];if((D==null?void 0:D.isTexture)===!0){const P=(d=(p=D.userData)==null?void 0:p.associations)==null?void 0:d.textures,G=r.parser.json.textures[P];if((c=G.extensions)!=null&&c[C]){const x=G.extensions[C];x&&i&&w.registerTexture(i,D,x.lods.length,x)}}}};const s=t.material;if(Array.isArray(s))for(const n of s)o(n);else o(s)}}tryParseMeshLOD(e,t){var o,s;if(t[Q]==!0)return;t[Q]=!0;const r=this.getUrl();if(!r)return;const i=(s=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:s[C];if(i&&r){const n=t.uuid;w.registerMesh(r,n,t,0,i.lods.length,i)}}}function ye(l,e,t,r){Z(e),j(t),t.register(o=>new w(o,l));const i=new _(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ee(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=C;exports.LODsManager=_;exports.NEEDLE_progressive=w;exports.patchModelViewer=ee;exports.registerPlugin=fe;exports.useNeedleProgressive=ye;
1
+ "use strict";var de=Object.defineProperty;var he=(a,e,t)=>e in a?de(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var u=(a,e,t)=>(he(a,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ge=require("three/examples/jsm/libs/meshopt_decoder.module.js"),pe=require("three/examples/jsm/loaders/DRACOLoader.js"),ye=require("three/examples/jsm/loaders/KTX2Loader.js"),g=require("three"),me=require("three/examples/jsm/loaders/GLTFLoader.js");let $="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ee="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch($+"draco_decoder.js",{method:"head"}).catch(a=>{$="./include/draco/",ee="./include/ktx2/"});function Le(a){$=a}function xe(a){ee=a}let U,Z,z;function te(a){U||(U=new pe.DRACOLoader,U.setDecoderPath($),U.setDecoderConfig({type:"js"})),z||(z=new ye.KTX2Loader,z.setTranscoderPath(ee)),Z||(Z=ge.MeshoptDecoder),a?z.detectSupport(a):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function re(a){a.dracoLoader||a.setDRACOLoader(U),a.ktx2Loader||a.setKTX2Loader(z),a.meshoptDecoder||a.setMeshoptDecoder(Z)}function se(a){const t=new URL(window.location.href).searchParams.get(a);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function De(a,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||a===void 0)return e;const t=a.lastIndexOf("/");if(t>=0){const r=a.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}function ae(a){var e;return((e=a.userData)==null?void 0:e["needle:raycast-mesh"])instanceof g.BufferGeometry?a.userData["needle:raycast-mesh"]:null}function le(a,e){(a.type==="Mesh"||a.type==="SkinnedMesh")&&(a.userData||(a.userData={}),a.userData["needle:raycast-mesh"]=e)}const R=new Array,k="NEEDLE_progressive",_=se("debugprogressive"),J=Symbol("needle-progressive-texture"),W=new Map,j=new Set;if(_){let a=function(){e+=1,console.log("Toggle LOD level",e,W),W.forEach((i,n)=>{for(const s of i.keys){const o=n[s];if(o.isBufferGeometry===!0){const l=S.getMeshLODInformation(o),d=l?Math.min(e,l.lods.length):0;n["DEBUG:LOD"]=e,S.assignMeshLOD(n,d),l&&(t=Math.max(t,l.lods.length-1))}else if(n.isMaterial===!0){n["DEBUG:LOD"]=e,S.assignTextureLOD(n,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&a(),i.key==="w"&&(r=!r,j&&j.forEach(n=>{n.name!="BackgroundCubeMaterial"&&"wireframe"in n&&(n.wireframe=r)}))})}function ie(a,e,t){var i;if(!_)return;W.has(a)||W.set(a,{keys:[],sourceId:t});const r=W.get(a);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const v=class{constructor(e,t){u(this,"parser");u(this,"url");_&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return k}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static getMaterialMinMaxLODsCount(e,t={min:1/0,max:-1}){if(Array.isArray(e)){for(const n of e)this.getMaterialMinMaxLODsCount(n,t);return t}let r=t.min,i=t.max;if(e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const n=e;for(const s of Object.keys(n.uniforms)){const o=n.uniforms[s].value;if((o==null?void 0:o.isTexture)===!0){const l=this.getAssignedLODInformation(o);if(l){const d=this.lodInfos.get(l.key);d&&d.lods&&(r=Math.min(r,d.lods.length),i=Math.max(i,d.lods.length))}}}}else if(e.isMaterial)for(const n of Object.keys(e)){const s=e[n];if((s==null?void 0:s.isTexture)===!0){const o=this.getAssignedLODInformation(s);if(o){const l=this.lodInfos.get(o.key);l&&l.lods&&(r=Math.min(r,l.lods.length),i=Math.max(i,l.lods.length))}}}return t.min=r,t.max=i,t}static hasLODLevelAvailable(e,t){var n;if(Array.isArray(e)){for(const s of e)if(this.hasLODLevelAvailable(s,t))return!0;return!1}if(e.isMaterial===!0){for(const s of Object.keys(e)){const o=e[s];if(o&&o.isTexture&&this.hasLODLevelAvailable(o,t))return!0}return!1}else if(e.isGroup===!0){for(const s of e.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,t))return!0}let r,i;if(e.isMesh?r=e.geometry:(e.isBufferGeometry||e.isTexture)&&(r=e),r&&(n=r==null?void 0:r.userData)!=null&&n.LODS){const s=r.userData.LODS;if(i=this.lodInfos.get(s.key),t===void 0)return i!=null;if(i)return Array.isArray(i.lods)?t<i.lods.length:t===0}return!1}static assignMeshLOD(e,t){var r;if(!e)return Promise.resolve(null);if(e instanceof g.Mesh||e.isMesh===!0){const i=e.geometry,n=this.getAssignedLODInformation(i);if(!n)return Promise.resolve(null);for(const s of R)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,v.getOrLoadLOD(i,t).then(s=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(s)){const o=n.index||0;s=s[o]}s&&i!=s&&s instanceof g.BufferGeometry&&(e.geometry=s,_&&ie(e,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else _&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",e);return Promise.resolve(null)}static assignTextureLOD(e,t=0){if(!e)return Promise.resolve(null);if(e instanceof g.Material||e.isMaterial===!0){const r=e,i=[],n=new Array;if(_&&j.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const o of Object.keys(s.uniforms)){const l=s.uniforms[o].value;if((l==null?void 0:l.isTexture)===!0){const d=this.assignTextureLODForSlot(l,t,r,o);i.push(d),n.push(o)}}}else for(const s of Object.keys(r)){const o=r[s];if((o==null?void 0:o.isTexture)===!0){const l=this.assignTextureLODForSlot(o,t,r,s);i.push(l),n.push(s)}}return Promise.all(i).then(s=>{const o=new Array;for(let l=0;l<s.length;l++){const d=s[l],h=n[l];d&&d.isTexture===!0?o.push({material:r,slot:h,texture:d,level:t}):o.push({material:r,slot:h,texture:null,level:t})}return o})}if(e instanceof g.Texture||e.isTexture===!0){const r=e;return this.assignTextureLODForSlot(r,t,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(e,t,r,i){if((e==null?void 0:e.isTexture)!==!0)return Promise.resolve(null);if(i==="glyphMap")return Promise.resolve(e);const n="LOD:requested level:"+i;return r&&(r[n]=t),v.getOrLoadLOD(e,t).then(s=>{if(r&&r[n]!=t||Array.isArray(s))return null;if((s==null?void 0:s.isTexture)===!0){if(s!=e&&(r&&i&&(r[i]=s),_&&i&&r)){const o=this.getAssignedLODInformation(e);o&&ie(r,i,o.url)}return s}else _=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(s=>(console.error("Error loading LOD",e,s),null))}afterRoot(e){var t,r;return _&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,n)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s){let o=!1;for(const l of this.parser.associations.keys())l.isTexture===!0&&this.parser.associations.get(l).textures===n&&(o=!0,v.registerTexture(this.url,l,n,s));o||this.parser.getDependency("texture",n).then(l=>{l&&v.registerTexture(this.url,l,n,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,n)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[k];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const l=this.parser.associations.get(o);l.meshes===n&&v.registerMesh(this.url,s.guid,o,s.lods.length,l.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var o,l,d,h;const r=_=="verbose",i=e.userData.LODS;if(!i)return null;const n=i==null?void 0:i.key;let s;if(e.isTexture===!0){const L=e;L.source&&L.source[J]&&(s=L.source[J])}if(s||(s=v.lodInfos.get(n)),s){if(t>0){let m=!1;const D=Array.isArray(s.lods);if(D&&t>=s.lods.length?m=!0:D||(m=!0),m)return this.lowresCache.get(n)}const L=Array.isArray(s.lods)?(o=s.lods[t])==null?void 0:o.path:s.lods;if(!L)return _&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const f=De(i.url,L);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const m=f+"_"+s.guid,D=this.previouslyLoaded.get(m);if(D!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${m}`);let w=await D.catch(F=>(console.error(`Error loading LOD ${t} from ${f}
2
+ `,F),null)),E=!1;if(w==null||(w instanceof g.Texture&&e instanceof g.Texture?(l=w.image)!=null&&l.data||(d=w.source)!=null&&d.data?w=this.copySettings(e,w):(E=!0,this.previouslyLoaded.delete(m)):w instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((h=w.attributes.position)!=null&&h.array||(E=!0,this.previouslyLoaded.delete(m)))),!E)return w}const O=s,M=new Promise(async(w,E)=>{const F=new me.GLTFLoader;re(F),_&&(await new Promise(p=>setTimeout(p,1e3)),r&&console.warn("Start loading (delayed) "+f,O.guid));let K=f;if(O&&Array.isArray(O.lods)){const p=O.lods[t];p.hash&&(K+="?v="+p.hash)}const B=await F.loadAsync(K).catch(p=>(console.error(`Error loading LOD ${t} from ${f}
3
+ `,p),null));if(!B)return null;const Y=B.parser;r&&console.log("Loading finished "+f,O.guid);let y=0;if(B.parser.json.textures){let p=!1;for(const c of B.parser.json.textures){if(c!=null&&c.extensions){const x=c==null?void 0:c.extensions[k];if(x!=null&&x.guid&&x.guid===O.guid){p=!0;break}}y++}if(p){let c=await Y.getDependency("texture",y);return c&&v.assignLODInformation(i.url,c,n,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+c.name+'"',f,y,c,m),e instanceof g.Texture&&(c=this.copySettings(e,c)),c&&(c.guid=O.guid),w(c)}else _&&console.warn("Could not find texture with guid",O.guid)}if(y=0,B.parser.json.meshes){let p=!1;for(const c of B.parser.json.meshes){if(c!=null&&c.extensions){const x=c==null?void 0:c.extensions[k];if(x!=null&&x.guid&&x.guid===O.guid){p=!0;break}}y++}if(p){const c=await Y.getDependency("mesh",y),x=O;if(r&&console.log(`Loaded Mesh "${c.name}"`,f,y,c,m),c.isMesh===!0){const T=c.geometry;return v.assignLODInformation(i.url,T,n,t,void 0,x.density),w(T)}else{const T=new Array;for(let C=0;C<c.children.length;C++){const I=c.children[C];if(I instanceof g.Mesh){const N=I.geometry;v.assignLODInformation(i.url,N,n,t,C,x.density),T.push(N)}}return w(T)}}}return w(null)});return this.previouslyLoaded.set(m,M),await M}else if(e instanceof g.Texture){r&&console.log("Load texture from uri: "+f);const D=await new g.TextureLoader().loadAsync(f);return D?(D.guid=s.guid,D.flipY=!1,D.needsUpdate=!0,D.colorSpace=e.colorSpace,r&&console.log(s,D)):_&&console.warn("failed loading",f),D}}else _&&console.warn(`Can not load LOD ${t}: no LOD info found for "${n}" ${e.name}`,e.type);return null}static assignLODInformation(e,t,r,i,n,s){if(!t)return;t.userData||(t.userData={});const o=new Me(e,r,i,n,s);t.userData.LODS=o,t.userData.LOD=i}static getAssignedLODInformation(e){var t;return((t=e==null?void 0:e.userData)==null?void 0:t.LODS)||null}static copySettings(e,t){return t=t.clone(),_&&console.warn(`Copying texture settings
4
+ `,e.uuid,`
5
+ `,t.uuid),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.mipmaps||(t.generateMipmaps=e.generateMipmaps),t}};let S=v;u(S,"registerTexture",(e,t,r,i)=>{_&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[J]=i);const n=i.guid;v.assignLODInformation(e,t,n,0,0,void 0),v.lodInfos.set(n,i),v.lowresCache.set(n,t)}),u(S,"registerMesh",(e,t,r,i,n,s)=>{var d;_&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),v.assignLODInformation(e,o,t,i,n,s.density),v.lodInfos.set(t,s);let l=v.lowresCache.get(t);l?l.push(r.geometry):l=[r.geometry],v.lowresCache.set(t,l),i>0&&!ae(r)&&le(r,o);for(const h of R)(d=h.onRegisteredNewMesh)==null||d.call(h,r,s)}),u(S,"lodInfos",new Map),u(S,"previouslyLoaded",new Map),u(S,"lowresCache",new Map);class Me{constructor(e,t,r,i,n){u(this,"url");u(this,"key");u(this,"level");u(this,"index");u(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),n!=null&&(this.density=n)}}const Q=se("debugprogressive"),Oe=se("noprogressive"),ne=Symbol("Needle:LODSManager"),P={mesh_lod:-1,texture_lod:-1},A=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new g.Matrix4);u(this,"cameraFrustrum",new g.Frustum);u(this,"targetTriangleDensity",2e5);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"_frame",0);u(this,"_originalRender");u(this,"_sphere",new g.Sphere);u(this,"_tempBox",new g.Box3);u(this,"_tempBox2",new g.Box3);u(this,"tempMatrix",new g.Matrix4);u(this,"_tempWorldPosition",new g.Vector3);u(this,"_tempBoxSize",new g.Vector3);u(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){R.push(e)}static removePlugin(e){const t=R.indexOf(e);t>=0&&R.splice(t,1)}static get(e){return e[ne]?e[ne]:new A(e)}get plugins(){return R}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,i){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const s=t._frame,o=e++;t.onBeforeRender(r,i,o,s),t._originalRender.call(this,r,i),t.onAfterRender(r,i,o,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var l,d;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),s=n.opaque;let o=!0;if(s.length===1){const h=s[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(o=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(o=!1),o){if(Oe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of s){if(f.material&&(((l=f.geometry)==null?void 0:l.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){Q&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}const L=n.transparent;for(const f of L){const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}}}updateLODs(e,t,r,i,n){var l,d;let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const h of R)(l=h.onBeforeUpdateLOD)==null||l.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,s,i,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod);let o=P.texture_lod;if(r.material&&o>=0){const h=r["DEBUG:LOD"];h!=null&&(o=h),this.loadProgressiveTextures(r.material,o)}for(const h of R)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lasLodLevel_Texture=P.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const n of e)this.loadProgressiveTextures(n,t);return}const r=e;let i=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(i=!0),i&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return S.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,i=e.max,n=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,i,n){var D;if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}let o=10+1;if(Q&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const l=S.getMeshLODInformation(t.geometry),d=l==null?void 0:l.lods,h=d&&d.length>0,L=S.getMaterialMinMaxLODsCount(t.material),f=(L==null?void 0:L.min)!=1/0&&L.min>0&&L.max>0;if(!h&&!f){n.mesh_lod=0,n.texture_lod=0;return}if(h||(o=0),!((D=this.cameraFrustrum)!=null&&D.intersectsObject(t))){n.mesh_lod=99,n.texture_lod=99;return}const m=t.geometry.boundingBox;if(m&&e.isPerspectiveCamera){const O=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(m),this._tempBox.applyMatrix4(t.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let c=y.x,x=y.y,T=p.x,C=p.y;const I=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;c=(c-V)*I+V,x=(x-q)*I+q,T=(T-V)*I+V,C=(C-q)*I+q;const ue=c<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),fe=x<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),H=Math.max(ue,fe);r.lastCentrality=(N-H)*(N-H)*(N-H)}else r.lastCentrality=1;const M=this._tempBox.getSize(this._tempBoxSize);M.multiplyScalar(.5),screen.availHeight>0&&M.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),M.x*=O.aspect;const G=e.matrixWorldInverse,w=this._tempBox2;w.copy(m),w.applyMatrix4(t.matrixWorld),w.applyMatrix4(G);const E=w.getSize(this._tempBox2Size),F=Math.max(E.x,E.y);if(Math.max(M.x,M.y)!=0&&F!=0&&(M.z=E.z/Math.max(E.x,E.y)*Math.max(M.x,M.y)),r.lastScreenCoverage=Math.max(M.x,M.y,M.z),r.lastScreenspaceVolume.copy(M),r.lastScreenCoverage*=r.lastCentrality,Q&&A.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=A.corner0,c=A.corner1,x=A.corner2,T=A.corner3;p.copy(this._tempBox.min),c.copy(this._tempBox.max),c.x=p.x,x.copy(this._tempBox.max),x.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=c.z=x.z=T.z=C,p.applyMatrix4(y),c.applyMatrix4(y),x.applyMatrix4(y),T.applyMatrix4(y),A.debugDrawLine(p,c,255),A.debugDrawLine(p,x,255),A.debugDrawLine(c,T,255),A.debugDrawLine(x,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<i){B=y;break}}B<o&&(o=B)}if(n.mesh_lod=o,L!=null&&L.min&&L.min>0&&L.max>0){const O=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=we(L.max,0,O)}else n.texture_lod=0}};let b=A;u(b,"debugDrawLine"),u(b,"corner0",new g.Vector3),u(b,"corner1",new g.Vector3),u(b,"corner2",new g.Vector3),u(b,"corner3",new g.Vector3),u(b,"_tempPtInside",new g.Vector3);function we(a,e,t){return a+(e-a)*t}class _e{constructor(){u(this,"frames",0);u(this,"lastLodLevel_Mesh",0);u(this,"lasLodLevel_Texture",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new g.Vector3);u(this,"lastCentrality",0)}}const oe=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(a){if(!a)return null;let e=null,t=null;for(let r=a;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),n=i.find(o=>o.toString()=="Symbol(renderer)"),s=i.find(o=>o.toString()=="Symbol(scene)");!e&&n!=null&&(e=a[n].threeRenderer),!t&&s!=null&&(t=a[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=b.get(e);if(b.addPlugin(new ve(a)),r.enable(),t){const i=t.camera||t.traverse(n=>n.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class ve{constructor(e){u(this,"modelviewer");u(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[X]==!0)return;t[X]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let n=function(o){var d,h,L;if(o[X]==!0)return;o[X]=!0,o.userData&&(o.userData.LOD=-1);const l=Object.keys(o);for(let f=0;f<l.length;f++){const m=l[f],D=o[m];if((D==null?void 0:D.isTexture)===!0){const O=(h=(d=D.userData)==null?void 0:d.associations)==null?void 0:h.textures,M=r.parser.json.textures[O];if(!M){console.warn("Texture data not found for texture index "+O);continue}if((L=M==null?void 0:M.extensions)!=null&&L[k]){const G=M.extensions[k];G&&i&&S.registerTexture(i,D,G.lods.length,G)}}}};const s=t.material;if(Array.isArray(s))for(const o of s)n(o);else n(s)}}tryParseMeshLOD(e,t){var n,s;if(t[oe]==!0)return;t[oe]=!0;const r=this.getUrl();if(!r)return;const i=(s=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[k];if(i&&r){const o=t.uuid;S.registerMesh(r,o,t,0,i.lods.length,i)}}}function Se(a,e,t,r){te(e),re(t),t.register(n=>new S(n,a));const i=b.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=k;exports.LODsManager=b;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=Le;exports.setKTX2TranscoderLocation=xe;exports.setRaycastMesh=le;exports.useNeedleProgressive=Se;
@@ -52,12 +52,19 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
52
52
  /** The name of the extension */
53
53
  get name(): string;
54
54
  static getMeshLODInformation(geo: BufferGeometry): NEEDLE_progressive_mesh_model | null;
55
+ static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?: {
56
+ min: number;
57
+ max: number;
58
+ }): {
59
+ min: number;
60
+ max: number;
61
+ };
55
62
  /** Check if a LOD level is available for a mesh or a texture
56
63
  * @param obj the mesh or texture to check
57
64
  * @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
58
65
  * @returns true if the LOD level is available (or if any LOD level is available if level is undefined)
59
66
  */
60
- static hasLODLevelAvailable(obj: Mesh | BufferGeometry | Texture | Material, level?: number): boolean;
67
+ static hasLODLevelAvailable(obj: Mesh | BufferGeometry | Texture | Material | Material[], level?: number): boolean;
61
68
  /** Load a different resolution of a mesh (if available)
62
69
  * @param context the context
63
70
  * @param source the sourceid of the file from which the mesh is loaded (this is usually the component's sourceId)
@@ -103,7 +110,6 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
103
110
  private static getOrLoadLOD;
104
111
  private static assignLODInformation;
105
112
  private static getAssignedLODInformation;
106
- private static readonly _copiedTextures;
107
113
  private static copySettings;
108
114
  }
109
115
  export {};
package/lib/extension.js CHANGED
@@ -1,7 +1,7 @@
1
- import { BufferGeometry, Material, Mesh, RawShaderMaterial, Texture, TextureLoader } from "three";
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, resolveUrl } from "./utils.js";
4
+ import { getParam, getRaycastMesh, resolveUrl, setRaycastMesh } from "./utils.js";
5
5
  // All of this has to be removed
6
6
  // import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
7
7
  // import { PromiseAllWithErrors, resolveUrl } from "../../engine_utils.js";
@@ -95,16 +95,67 @@ export class NEEDLE_progressive {
95
95
  }
96
96
  return null;
97
97
  }
98
+ static getMaterialMinMaxLODsCount(material, minmax = { min: Infinity, max: -1 }) {
99
+ if (Array.isArray(material)) {
100
+ for (const mat of material) {
101
+ this.getMaterialMinMaxLODsCount(mat, minmax);
102
+ }
103
+ return minmax;
104
+ }
105
+ let min = minmax.min;
106
+ let max = minmax.max;
107
+ if (material.type === "ShaderMaterial" || material.type === "RawShaderMaterial") {
108
+ const mat = material;
109
+ for (const slot of Object.keys(mat.uniforms)) {
110
+ const val = mat.uniforms[slot].value;
111
+ if (val?.isTexture === true) {
112
+ const info = this.getAssignedLODInformation(val);
113
+ if (info) {
114
+ const model = this.lodInfos.get(info.key);
115
+ if (model && model.lods) {
116
+ min = Math.min(min, model.lods.length);
117
+ max = Math.max(max, model.lods.length);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ else if (material.isMaterial) {
124
+ for (const slot of Object.keys(material)) {
125
+ const val = material[slot];
126
+ if (val?.isTexture === true) {
127
+ const info = this.getAssignedLODInformation(val);
128
+ if (info) {
129
+ const model = this.lodInfos.get(info.key);
130
+ if (model && model.lods) {
131
+ min = Math.min(min, model.lods.length);
132
+ max = Math.max(max, model.lods.length);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ minmax.min = min;
139
+ minmax.max = max;
140
+ return minmax;
141
+ }
98
142
  /** Check if a LOD level is available for a mesh or a texture
99
143
  * @param obj the mesh or texture to check
100
144
  * @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
101
145
  * @returns true if the LOD level is available (or if any LOD level is available if level is undefined)
102
146
  */
103
147
  static hasLODLevelAvailable(obj, level) {
148
+ if (Array.isArray(obj)) {
149
+ for (const mat of obj) {
150
+ if (this.hasLODLevelAvailable(mat, level))
151
+ return true;
152
+ }
153
+ return false;
154
+ }
104
155
  if (obj.isMaterial === true) {
105
156
  for (const slot of Object.keys(obj)) {
106
157
  const val = obj[slot];
107
- if (val.isTexture) {
158
+ if (val && val.isTexture) {
108
159
  if (this.hasLODLevelAvailable(val, level))
109
160
  return true;
110
161
  }
@@ -219,10 +270,12 @@ export class NEEDLE_progressive {
219
270
  const slots = new Array();
220
271
  if (debug)
221
272
  debug_materials.add(material);
222
- if (material instanceof RawShaderMaterial) {
273
+ // Handle custom shaders / uniforms progressive textures. This includes support for VRM shaders
274
+ if (material.uniforms && material.isRawShaderMaterial || material.isShaderMaterial === true) {
223
275
  // iterate uniforms of custom shaders
224
- for (const slot of Object.keys(material.uniforms)) {
225
- const val = material.uniforms[slot].value;
276
+ const shaderMaterial = material;
277
+ for (const slot of Object.keys(shaderMaterial.uniforms)) {
278
+ const val = shaderMaterial.uniforms[slot].value;
226
279
  if (val?.isTexture === true) {
227
280
  const task = this.assignTextureLODForSlot(val, level, material, slot);
228
281
  promises.push(task);
@@ -265,8 +318,18 @@ export class NEEDLE_progressive {
265
318
  if (current?.isTexture !== true)
266
319
  return Promise.resolve(null);
267
320
  // if (debug) console.log("-----------\n", "FIND", material?.name, slot, current?.name, current?.userData, current, material);
268
- // const info = this.onProgressiveLoadStart(context, source, material, slot);
321
+ if (slot === "glyphMap") {
322
+ return Promise.resolve(current);
323
+ }
324
+ // save which texture level was requested last
325
+ const key = "LOD:requested level:" + slot;
326
+ if (material)
327
+ material[key] = level;
269
328
  return NEEDLE_progressive.getOrLoadLOD(current, level).then(tex => {
329
+ // check if the requested level has changed in the meantime
330
+ if (material && material[key] != level)
331
+ return null;
332
+ // if (material) delete material[key];
270
333
  // this can currently not happen
271
334
  if (Array.isArray(tex))
272
335
  return null;
@@ -392,6 +455,9 @@ export class NEEDLE_progressive {
392
455
  else
393
456
  existing = [mesh.geometry];
394
457
  NEEDLE_progressive.lowresCache.set(key, existing);
458
+ if (level > 0 && !getRaycastMesh(mesh)) {
459
+ setRaycastMesh(mesh, geometry);
460
+ }
395
461
  for (const plugin of plugins) {
396
462
  plugin.onRegisteredNewMesh?.(mesh, ext);
397
463
  }
@@ -435,7 +501,7 @@ export class NEEDLE_progressive {
435
501
  }
436
502
  }
437
503
  /** the unresolved LOD url */
438
- const unresolved_lod_url = Array.isArray(progressiveInfo.lods) ? progressiveInfo.lods[level].path : progressiveInfo.lods;
504
+ const unresolved_lod_url = Array.isArray(progressiveInfo.lods) ? progressiveInfo.lods[level]?.path : progressiveInfo.lods;
439
505
  // check if we have a uri
440
506
  if (!unresolved_lod_url) {
441
507
  if (debug && !progressiveInfo["missing:uri"]) {
@@ -535,6 +601,9 @@ export class NEEDLE_progressive {
535
601
  }
536
602
  if (found) {
537
603
  let tex = await parser.getDependency("texture", index);
604
+ if (tex) {
605
+ NEEDLE_progressive.assignLODInformation(LOD.url, tex, LODKEY, level, undefined, undefined);
606
+ }
538
607
  if (debugverbose)
539
608
  console.log("change \"" + current.name + "\" → \"" + tex.name + "\"", lod_url, index, tex, KEY);
540
609
  if (current instanceof Texture)
@@ -544,6 +613,9 @@ export class NEEDLE_progressive {
544
613
  }
545
614
  return resolve(tex);
546
615
  }
616
+ else if (debug) {
617
+ console.warn("Could not find texture with guid", ext.guid);
618
+ }
547
619
  }
548
620
  index = 0;
549
621
  if (gltf.parser.json.meshes) {
@@ -630,22 +702,43 @@ export class NEEDLE_progressive {
630
702
  static getAssignedLODInformation(res) {
631
703
  return res?.userData?.LODS || null;
632
704
  }
633
- static _copiedTextures = new Map();
705
+ // private static readonly _copiedTextures: WeakMap<Texture, Texture> = new Map();
634
706
  static copySettings(source, target) {
707
+ // const existingCopy = source["LODS:COPY"];
635
708
  // don't copy again if the texture was processed before
636
- const existingClone = this._copiedTextures.get(source);
637
- if (existingClone) {
638
- return existingClone;
639
- }
709
+ // we clone the source if it's animated
710
+ // const existingClone = this._copiedTextures.get(source);
711
+ // if (existingClone) {
712
+ // return existingClone;
713
+ // }
640
714
  // We need to clone e.g. when the same texture is used multiple times (but with e.g. different wrap settings)
641
715
  // This is relatively cheap since it only stores settings
642
716
  // This should only happen once ever for every texture
643
- target = target.clone();
644
- this._copiedTextures.set(source, target);
717
+ // const original = target;
718
+ {
719
+ target = target.clone();
720
+ if (debug)
721
+ console.warn("Copying texture settings\n", source.uuid, "\n", target.uuid);
722
+ }
723
+ // else {
724
+ // source = existingCopy;
725
+ // }
726
+ // this._copiedTextures.set(original, target);
645
727
  // we re-use the offset and repeat settings because it might be animated
646
728
  target.offset = source.offset;
647
729
  target.repeat = source.repeat;
648
730
  target.colorSpace = source.colorSpace;
731
+ target.magFilter = source.magFilter;
732
+ target.minFilter = source.minFilter;
733
+ target.wrapS = source.wrapS;
734
+ target.wrapT = source.wrapT;
735
+ target.flipY = source.flipY;
736
+ target.anisotropy = source.anisotropy;
737
+ if (!target.mipmaps)
738
+ target.generateMipmaps = source.generateMipmaps;
739
+ // if (!target.userData) target.userData = {};
740
+ // target["LODS:COPY"] = source;
741
+ // related: NE-4937
649
742
  return target;
650
743
  }
651
744
  }
package/lib/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- export * from "./extension.js";
2
- export * from "./plugins/index.js";
3
- export { LODsManager } from "./lods_manager.js";
4
1
  import { WebGLRenderer } from "three";
5
2
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
6
3
  import { LODsManager } from "./lods_manager.js";
4
+ export * from "./extension.js";
5
+ export * from "./plugins/index.js";
6
+ export { LODsManager } from "./lods_manager.js";
7
+ export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
8
+ export { getRaycastMesh, setRaycastMesh } from "./utils.js";
7
9
  declare class UseNeedleGLTFProgressive {
8
- /** */
9
10
  enableLODsManager?: boolean;
10
11
  }
11
12
  /** Use this function to enable progressive loading of gltf models.
package/lib/index.js CHANGED
@@ -1,9 +1,11 @@
1
- export * from "./extension.js";
2
- export * from "./plugins/index.js";
3
- export { LODsManager } from "./lods_manager.js";
4
1
  import { addDracoAndKTX2Loaders, createLoaders } from "./loaders.js";
5
2
  import { NEEDLE_progressive } from "./extension.js";
6
3
  import { LODsManager } from "./lods_manager.js";
4
+ export * from "./extension.js";
5
+ export * from "./plugins/index.js";
6
+ export { LODsManager } from "./lods_manager.js";
7
+ export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
8
+ export { getRaycastMesh, setRaycastMesh } from "./utils.js";
7
9
  ;
8
10
  /** Use this function to enable progressive loading of gltf models.
9
11
  * @param url The url of the gltf model.
@@ -24,7 +26,7 @@ export function useNeedleProgressive(url, renderer, loader, opts) {
24
26
  createLoaders(renderer);
25
27
  addDracoAndKTX2Loaders(loader);
26
28
  loader.register(p => new NEEDLE_progressive(p, url));
27
- const lod = new LODsManager(renderer);
29
+ const lod = LODsManager.get(renderer);
28
30
  if (opts?.enableLODsManager !== false) {
29
31
  lod.enable();
30
32
  }
package/lib/loaders.d.ts CHANGED
@@ -1,4 +1,14 @@
1
1
  import { WebGLRenderer } from 'three';
2
2
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
3
+ /**
4
+ * Set the location of the Draco decoder.
5
+ * @default 'https://www.gstatic.com/draco/versioned/decoders/1.4.1/'
6
+ */
7
+ export declare function setDracoDecoderLocation(location: string): void;
8
+ /**
9
+ * Set the location of the KTX2 transcoder.
10
+ * @default 'https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/'
11
+ */
12
+ export declare function setKTX2TranscoderLocation(location: string): void;
3
13
  export declare function createLoaders(renderer: WebGLRenderer): void;
4
14
  export declare function addDracoAndKTX2Loaders(loader: GLTFLoader): void;
package/lib/loaders.js CHANGED
@@ -1,8 +1,27 @@
1
1
  import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
2
2
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
3
3
  import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
4
- const DEFAULT_DRACO_DECODER_LOCATION = 'https://www.gstatic.com/draco/versioned/decoders/1.4.1/';
5
- const DEFAULT_KTX2_TRANSCODER_LOCATION = 'https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/';
4
+ let DEFAULT_DRACO_DECODER_LOCATION = 'https://www.gstatic.com/draco/versioned/decoders/1.4.1/';
5
+ let DEFAULT_KTX2_TRANSCODER_LOCATION = 'https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/';
6
+ fetch(DEFAULT_DRACO_DECODER_LOCATION + "draco_decoder.js", { method: "head" })
7
+ .catch(_ => {
8
+ DEFAULT_DRACO_DECODER_LOCATION = "./include/draco/";
9
+ DEFAULT_KTX2_TRANSCODER_LOCATION = "./include/ktx2/";
10
+ });
11
+ /**
12
+ * Set the location of the Draco decoder.
13
+ * @default 'https://www.gstatic.com/draco/versioned/decoders/1.4.1/'
14
+ */
15
+ export function setDracoDecoderLocation(location) {
16
+ DEFAULT_DRACO_DECODER_LOCATION = location;
17
+ }
18
+ /**
19
+ * Set the location of the KTX2 transcoder.
20
+ * @default 'https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/'
21
+ */
22
+ export function setKTX2TranscoderLocation(location) {
23
+ DEFAULT_KTX2_TRANSCODER_LOCATION = location;
24
+ }
6
25
  let dracoLoader;
7
26
  let meshoptDecoder;
8
27
  let ktx2Loader;