@needle-tools/gltf-progressive 1.0.0-alpha.13 → 1.0.0-alpha.15

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,3 @@
1
- var ne=Object.defineProperty,ie=(r,e,t)=>e in r?ne(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,c=(r,e,t)=>(ie(r,typeof e!="symbol"?e+"":e,t),t);import{Mesh as W,BufferGeometry as $,Material as ae,Texture as C,TextureLoader as le,Matrix4 as ee,Frustum as ue,Sphere as ce,Box3 as re,Vector3 as B}from"three";import{GLTFLoader as de}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as he}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as fe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as ge}from"three/examples/jsm/loaders/KTX2Loader.js";let U="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",q="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(U+"draco_decoder.js",{method:"head"}).catch(r=>{U="./include/draco/",q="./include/ktx2/"});function me(r){U=r}function pe(r){q=r}let R,V,G;function X(r){R||(R=new fe,R.setDecoderPath(U),R.setDecoderConfig({type:"js"})),G||(G=new ge,G.setTranscoderPath(q)),V||(V=he),r?G.detectSupport(r):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function K(r){r.dracoLoader||r.setDRACOLoader(R),r.ktx2Loader||r.setKTX2Loader(G),r.meshoptDecoder||r.setMeshoptDecoder(V)}function H(r){const e=new URL(window.location.href).searchParams.get(r);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function ye(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 Y=new Array;function Le(r){Y.push(r)}const P="NEEDLE_progressive",v=H("debugprogressive"),J=Symbol("needle-progressive-texture"),N=new Map,Q=new Set;if(v){let r=function(){e+=1,console.log("Toggle LOD level",e,N),N.forEach((n,s)=>{for(const a of n.keys){const i=s[a];if(i.isBufferGeometry===!0){const l=w.getMeshLODInformation(i),d=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=e,w.assignMeshLOD(s,d),l&&(t=Math.max(t,l.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.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,Q&&Q.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=o)}))})}function te(r,e,t){var o;if(!v)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 O=class{constructor(r,e){c(this,"parser"),c(this,"url"),v&&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 a=r[s];if(a.isTexture&&this.hasLODLevelAvailable(a,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 W||r.isMesh===!0){const o=r.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of Y)(t=s.onBeforeGetLODMesh)==null||t.call(s,r,e);return r["LOD:requested level"]=e,O.getOrLoadLOD(o,e).then(s=>{if(r["LOD:requested level"]===e){if(delete r["LOD:requested level"],Array.isArray(s)){const a=n.index||0;s=s[a]}s&&o!=s&&s instanceof $&&(r.geometry=s,v&&te(r,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",r,s),null))}else v&&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 ae||r.isMaterial===!0){const t=r,o=[],n=new Array;if(v&&Q.add(t),t.uniforms&&t.isRawShaderMaterial||t.isShaderMaterial===!0){const s=t;for(const a of Object.keys(s.uniforms)){const i=s.uniforms[a].value;if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,t,a);o.push(l),n.push(a)}}}else for(const s of Object.keys(t)){const a=t[s];if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,t,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 l=s[i],d=n[i];l&&l.isTexture===!0?a.push({material:t,slot:d,texture:l,level:e}):a.push({material:t,slot:d,texture:null,level:e})}return a})}if(r instanceof C||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):O.getOrLoadLOD(r,e).then(n=>{if(Array.isArray(n))return null;if(n?.isTexture===!0){if(n!=r&&(t&&o&&(t[o]=n),v&&o&&t)){const s=this.getAssignedLODInformation(r);s&&te(t,o,s.url)}return n}else v=="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 v&&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 a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===n&&(a=!0,O.registerTexture(this.url,i,n,s));a||this.parser.getDependency("texture",n).then(i=>{i&&O.registerTexture(this.url,i,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 a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===n&&O.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,o,n;const s=v=="verbose",a=r.userData.LODS;if(!a)return null;const i=a?.key;let l;if(r.isTexture===!0){const d=r;d.source&&d.source[J]&&(l=d.source[J])}if(l||(l=O.lodInfos.get(i)),l){if(e>0){let u=!1;const p=Array.isArray(l.lods);if(p&&e>=l.lods.length?u=!0:p||(u=!0),u)return this.lowresCache.get(i)}const d=Array.isArray(l.lods)?l.lods[e].path:l.lods;if(!d)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=ye(a.url,d);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const u=h+"_"+l.guid,p=this.previouslyLoaded.get(u);if(p!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let y=await p.catch(m=>(console.error(`Error loading LOD ${e} from ${h}
2
- `,m),null)),g=!1;if(y==null||(y instanceof C&&r instanceof C?(t=y.image)!=null&&t.data||(o=y.source)!=null&&o.data?y=this.copySettings(r,y):(g=!0,this.previouslyLoaded.delete(u)):y instanceof $&&r instanceof $&&((n=y.attributes.position)!=null&&n.array||(g=!0,this.previouslyLoaded.delete(u)))),!g)return y}const L=l,A=new Promise(async(y,g)=>{const m=new de;K(m),v&&(await new Promise(x=>setTimeout(x,1e3)),s&&console.warn("Start loading (delayed) "+h,L.guid));let b=h;if(L&&Array.isArray(L.lods)){const x=L.lods[e];x.hash&&(b+="?v="+x.hash)}const D=await m.loadAsync(b).catch(x=>(console.error(`Error loading LOD ${e} from ${h}
3
- `,x),null));if(!D)return null;const S=D.parser;s&&console.log("Loading finished "+h,L.guid);let T=0;if(D.parser.json.textures){let x=!1;for(const f of D.parser.json.textures){if(f!=null&&f.extensions){const M=f?.extensions[P];if(M!=null&&M.guid&&M.guid===L.guid){x=!0;break}}T++}if(x){let f=await S.getDependency("texture",T);return s&&console.log('change "'+r.name+'" \u2192 "'+f.name+'"',h,T,f,u),r instanceof C&&(f=this.copySettings(r,f)),f&&(f.guid=L.guid),y(f)}}if(T=0,D.parser.json.meshes){let x=!1;for(const f of D.parser.json.meshes){if(f!=null&&f.extensions){const M=f?.extensions[P];if(M!=null&&M.guid&&M.guid===L.guid){x=!0;break}}T++}if(x){const f=await S.getDependency("mesh",T),M=L;if(s&&console.log(`Loaded Mesh "${f.name}"`,h,T,f,u),f.isMesh===!0){const _=f.geometry;return O.assignLODInformation(a.url,_,i,e,void 0,M.density),y(_)}else{const _=new Array;for(let k=0;k<f.children.length;k++){const F=f.children[k];if(F instanceof W){const j=F.geometry;O.assignLODInformation(a.url,j,i,e,k,M.density),_.push(j)}}return y(_)}}}return y(null)});return this.previouslyLoaded.set(u,A),await A}else if(r instanceof C){s&&console.log("Load texture from uri: "+h);const u=await new le().loadAsync(h);return u?(u.guid=l.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=r.colorSpace,s&&console.log(l,u)):v&&console.warn("failed loading",h),u}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,o,n,s){if(!e)return;e.userData||(e.userData={});const a=new De(r,t,o,n,s);e.userData.LODS=a,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.magFilter=r.magFilter,e.minFilter=r.minFilter,e.wrapS=r.wrapS,e.wrapT=r.wrapT,e.flipY=r.flipY,e.anisotropy=r.anisotropy,e.generateMipmaps=r.generateMipmaps,e)}};let w=O;c(w,"registerTexture",(r,e,t,o)=>{v&&console.log("> Progressive: register texture",t,e.name,e.uuid,e,o),e.source&&(e.source[J]=o);const n=o.guid;O.assignLODInformation(r,e,n,0,0,void 0),O.lodInfos.set(n,o),O.lowresCache.set(n,e)}),c(w,"registerMesh",(r,e,t,o,n,s)=>{var a;v&&console.log("> Progressive: register mesh",n,t.name,s,t.uuid,t);const i=t.geometry;i.userData||(i.userData={}),O.assignLODInformation(r,i,e,o,n,s.density),O.lodInfos.set(e,s);let l=O.lowresCache.get(e);l?l.push(t.geometry):l=[t.geometry],O.lowresCache.set(e,l);for(const d of Y)(a=d.onRegisteredNewMesh)==null||a.call(d,t,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map),c(w,"_copiedTextures",new Map);class De{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 Z=H("debugprogressive"),xe=H("noprogressive"),E=class{constructor(r){c(this,"renderer"),c(this,"projectionScreenMatrix",new ee),c(this,"cameraFrustrum",new ue),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"plugins",[]),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new ce),c(this,"_tempBox",new re),c(this,"tempMatrix",new ee),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;X(this.renderer),this.renderer.render=function(t,o){e.renderer.getRenderTarget()==null&&(r=0,e._frame+=1);const n=e._frame,s=r++;e.onBeforeRender(t,o,s,n),e._originalRender.call(this,t,o),e.onAfterRender(t,o,s,n)}}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(this.pause)return;const a=this.renderer.renderLists.get(r,0),i=a.opaque;let l=!0;if(i.length===1){const d=i[0].material;(d.name==="EffectMaterial"||d.name==="CopyShader")&&(l=!1)}if(l){if(xe||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const d=1e5;for(const u of i){if(u.material&&(((n=u.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=u.geometry)==null?void 0:s.type)==="BufferGeometry")&&(u.material.name==="SphericalGaussianBlur"||u.material.name=="BackgroundCubeMaterial"||u.material.name==="CubemapFromEquirect"||u.material.name==="EquirectangularToCubeUV")){Z&&(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",u,u.material.name,u.material.type)));continue}const p=u.object;(p instanceof W||p.isMesh)&&this.updateLODs(r,e,p,d)}const h=a.transparent;for(const u of h){const p=u.object;(p instanceof W||p.isMesh)&&this.updateLODs(r,e,p,d)}}}updateLODs(r,e,t,o){var n,s;for(const d of this.plugins)(n=d.onBeforeUpdateLOD)==null||n.call(d,this.renderer,r,e,t);let a=t.userData.LOD_state;a||(a=new ve,t.userData.LOD_state=a);let i=this.calculateLodLevel(e,t,a,o);i=Math.round(i),i>=0&&this.loadProgressiveMeshes(t,i);let l=0;if(t.material){const d=t["DEBUG:LOD"];if(d!=null&&(l=d),Array.isArray(t.material))for(const h of t.material)this.loadProgressiveTextures(h,l);else this.loadProgressiveTextures(t.material,l)}for(const d of this.plugins)(s=d.onAfterUpdatedLOD)==null||s.call(d,this.renderer,r,e,t,i);a.lastLodLevel=i}loadProgressiveTextures(r,e){return r&&r.userData&&r.userData.LOD!==e?(r.userData.LOD=e,w.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 w.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(Z&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=w.getMeshLODInformation(e.geometry),i=a?.lods;if(!i||i.length<=0||!((n=this.cameraFrustrum)!=null&&n.intersectsObject(e)))return 99;const l=e.geometry.boundingBox;if(l&&r.isPerspectiveCamera){const d=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(l),this._tempBox.applyMatrix4(e.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&d.fov>70){const g=this._tempBox.min,m=this._tempBox.max;let b=g.x,D=g.y,S=m.x,T=m.y;const x=2,f=1.5,M=(g.x+m.x)*.5,_=(g.y+m.y)*.5;b=(b-M)*x+M,D=(D-_)*x+_,S=(S-M)*x+M,T=(T-_)*x+_;const k=b<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(m.x)),F=D<0&&T>0?0:Math.min(Math.abs(g.y),Math.abs(m.y)),j=Math.max(k,F);t.lastCentrality=(f-j)*(f-j)*(f-j)}else t.lastCentrality=1;const h=this._tempBox.getSize(this._tempBoxSize);h.multiplyScalar(.5),screen.availHeight>0&&h.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),h.x*=d.aspect;const u=r.matrixWorldInverse,p=new re;p.copy(l),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(u);const L=p.getSize(this._tempBox2Size),A=Math.max(L.x,L.y);if(Math.max(h.x,h.y)!=0&&A!=0&&(h.z=L.z/Math.max(L.x,L.y)*Math.max(h.x,h.y)),t.lastScreenCoverage=Math.max(h.x,h.y,h.z),t.lastScreenspaceVolume.copy(h),t.lastScreenCoverage*=t.lastCentrality,Z&&E.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const m=E.corner0,b=E.corner1,D=E.corner2,S=E.corner3;m.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=m.x,D.copy(this._tempBox.max),D.y=m.y,S.copy(this._tempBox.max);const T=(m.z+S.z)*.5;m.z=b.z=D.z=S.z=T,m.applyMatrix4(g),b.applyMatrix4(g),D.applyMatrix4(g),S.applyMatrix4(g),E.debugDrawLine(m,b,255),E.debugDrawLine(m,D,255),E.debugDrawLine(b,S,255),E.debugDrawLine(D,S,255)}let y=999;if(i&&t.lastScreenCoverage>0){for(let g=0;g<i.length;g++)if(i[g].density/t.lastScreenCoverage<o){y=g;break}}y<s&&(s=y)}}return s}};let I=E;c(I,"debugDrawLine"),c(I,"corner0",new B),c(I,"corner1",new B),c(I,"corner2",new B),c(I,"corner3",new B);class ve{constructor(){c(this,"lastLodLevel",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new B),c(this,"lastCentrality",0)}}const se=Symbol("NEEDLE_mesh_lod"),z=Symbol("NEEDLE_texture_lod");function oe(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(i=>i.toString()=="Symbol(renderer)"),a=n.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=r[s].threeRenderer),!t&&a!=null&&(t=r[a])}if(e){console.log("Adding Needle LODs to modelviewer");const o=new I(e);if(o.plugins.push(new Oe(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 Oe{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(i){var l,d,h;if(i[z]==!0)return;i[z]=!0,i.userData&&(i.userData.LOD=-1);const u=Object.keys(i);for(let p=0;p<u.length;p++){const L=u[p],A=i[L];if(A?.isTexture===!0){const y=(d=(l=A.userData)==null?void 0:l.associations)==null?void 0:d.textures,g=o.parser.json.textures[y];if((h=g.extensions)!=null&&h[P]){const m=g.extensions[P];m&&n&&w.registerTexture(n,A,m.lods.length,m)}}}};const a=t.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,t){var o,n;if(t[se]==!0)return;t[se]=!0;const s=this.getUrl();if(!s)return;const a=(n=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[P];if(a&&s){const i=t.uuid;w.registerMesh(s,i,t,0,a.lods.length,a)}}}function we(r,e,t,o){X(e),K(t),t.register(s=>new w(s,r));const n=new I(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{oe(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,I as LODsManager,w as NEEDLE_progressive,K as addDracoAndKTX2Loaders,X as createLoaders,oe as patchModelViewer,Le as registerPlugin,me as setDracoDecoderLocation,pe as setKTX2TranscoderLocation,we as useNeedleProgressive};
1
+ var le=Object.defineProperty,ue=(r,e,t)=>e in r?le(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,c=(r,e,t)=>(ue(r,typeof e!="symbol"?e+"":e,t),t);import{MeshoptDecoder as ce}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as de}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as he}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as U,Mesh as z,Material as fe,Texture as C,TextureLoader as ge,Matrix4 as ee,Frustum as me,Sphere as pe,Box3 as re,Vector3 as I}from"three";import{GLTFLoader as ye}from"three/examples/jsm/loaders/GLTFLoader.js";let $="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",V="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch($+"draco_decoder.js",{method:"head"}).catch(r=>{$="./include/draco/",V="./include/ktx2/"});function De(r){$=r}function xe(r){V=r}let G,X,N;function K(r){G||(G=new de,G.setDecoderPath($),G.setDecoderConfig({type:"js"})),N||(N=new he,N.setTranscoderPath(V)),X||(X=ce),r?N.detectSupport(r):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function H(r){r.dracoLoader||r.setDRACOLoader(G),r.ktx2Loader||r.setKTX2Loader(N),r.meshoptDecoder||r.setMeshoptDecoder(X)}function Y(r){const e=new URL(window.location.href).searchParams.get(r);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Le(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}function te(r){var e;return((e=r.userData)==null?void 0:e["needle:raycast-mesh"])instanceof U?r.userData["needle:raycast-mesh"]:null}function se(r,e){(r.type==="Mesh"||r.type==="SkinnedMesh")&&(r.userData||(r.userData={}),r.userData["needle:raycast-mesh"]=e)}const R=new Array,P="NEEDLE_progressive",v=Y("debugprogressive"),J=Symbol("needle-progressive-texture"),F=new Map,Q=new Set;if(v){let r=function(){e+=1,console.log("Toggle LOD level",e,F),F.forEach((n,s)=>{for(const a of n.keys){const i=s[a];if(i.isBufferGeometry===!0){const l=w.getMeshLODInformation(i),d=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=e,w.assignMeshLOD(s,d),l&&(t=Math.max(t,l.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.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,Q&&Q.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=o)}))})}function oe(r,e,t){var o;if(!v)return;F.has(r)||F.set(r,{keys:[],sourceId:t});const n=F.get(r);((o=n?.keys)==null?void 0:o.includes(e))==!1&&n.keys.push(e)}const O=class{constructor(r,e){c(this,"parser"),c(this,"url"),v&&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 a=r[s];if(a.isTexture&&this.hasLODLevelAvailable(a,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 z||r.isMesh===!0){const o=r.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of R)(t=s.onBeforeGetLODMesh)==null||t.call(s,r,e);return r["LOD:requested level"]=e,O.getOrLoadLOD(o,e).then(s=>{if(r["LOD:requested level"]===e){if(delete r["LOD:requested level"],Array.isArray(s)){const a=n.index||0;s=s[a]}s&&o!=s&&s instanceof U&&(r.geometry=s,v&&oe(r,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",r,s),null))}else v&&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 fe||r.isMaterial===!0){const t=r,o=[],n=new Array;if(v&&Q.add(t),t.uniforms&&t.isRawShaderMaterial||t.isShaderMaterial===!0){const s=t;for(const a of Object.keys(s.uniforms)){const i=s.uniforms[a].value;if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,t,a);o.push(l),n.push(a)}}}else for(const s of Object.keys(t)){const a=t[s];if(a?.isTexture===!0){const i=this.assignTextureLODForSlot(a,e,t,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 l=s[i],d=n[i];l&&l.isTexture===!0?a.push({material:t,slot:d,texture:l,level:e}):a.push({material:t,slot:d,texture:null,level:e})}return a})}if(r instanceof C||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):O.getOrLoadLOD(r,e).then(n=>{if(Array.isArray(n))return null;if(n?.isTexture===!0){if(n!=r&&(t&&o&&(t[o]=n),v&&o&&t)){const s=this.getAssignedLODInformation(r);s&&oe(t,o,s.url)}return n}else v=="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 v&&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 a=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i).textures===n&&(a=!0,O.registerTexture(this.url,i,n,s));a||this.parser.getDependency("texture",n).then(i=>{i&&O.registerTexture(this.url,i,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 a of this.parser.associations.keys())if(a.isMesh){const i=this.parser.associations.get(a);i.meshes===n&&O.registerMesh(this.url,s.guid,a,s.lods.length,i.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,o,n;const s=v=="verbose",a=r.userData.LODS;if(!a)return null;const i=a?.key;let l;if(r.isTexture===!0){const d=r;d.source&&d.source[J]&&(l=d.source[J])}if(l||(l=O.lodInfos.get(i)),l){if(e>0){let u=!1;const p=Array.isArray(l.lods);if(p&&e>=l.lods.length?u=!0:p||(u=!0),u)return this.lowresCache.get(i)}const d=Array.isArray(l.lods)?l.lods[e].path:l.lods;if(!d)return v&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=Le(a.url,d);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const u=h+"_"+l.guid,p=this.previouslyLoaded.get(u);if(p!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let y=await p.catch(m=>(console.error(`Error loading LOD ${e} from ${h}
2
+ `,m),null)),f=!1;if(y==null||(y instanceof C&&r instanceof C?(t=y.image)!=null&&t.data||(o=y.source)!=null&&o.data?y=this.copySettings(r,y):(f=!0,this.previouslyLoaded.delete(u)):y instanceof U&&r instanceof U&&((n=y.attributes.position)!=null&&n.array||(f=!0,this.previouslyLoaded.delete(u)))),!f)return y}const D=l,A=new Promise(async(y,f)=>{const m=new ye;H(m),v&&(await new Promise(L=>setTimeout(L,1e3)),s&&console.warn("Start loading (delayed) "+h,D.guid));let b=h;if(D&&Array.isArray(D.lods)){const L=D.lods[e];L.hash&&(b+="?v="+L.hash)}const x=await m.loadAsync(b).catch(L=>(console.error(`Error loading LOD ${e} from ${h}
3
+ `,L),null));if(!x)return null;const S=x.parser;s&&console.log("Loading finished "+h,D.guid);let T=0;if(x.parser.json.textures){let L=!1;for(const g of x.parser.json.textures){if(g!=null&&g.extensions){const M=g?.extensions[P];if(M!=null&&M.guid&&M.guid===D.guid){L=!0;break}}T++}if(L){let g=await S.getDependency("texture",T);return s&&console.log('change "'+r.name+'" \u2192 "'+g.name+'"',h,T,g,u),r instanceof C&&(g=this.copySettings(r,g)),g&&(g.guid=D.guid),y(g)}}if(T=0,x.parser.json.meshes){let L=!1;for(const g of x.parser.json.meshes){if(g!=null&&g.extensions){const M=g?.extensions[P];if(M!=null&&M.guid&&M.guid===D.guid){L=!0;break}}T++}if(L){const g=await S.getDependency("mesh",T),M=D;if(s&&console.log(`Loaded Mesh "${g.name}"`,h,T,g,u),g.isMesh===!0){const E=g.geometry;return O.assignLODInformation(a.url,E,i,e,void 0,M.density),y(E)}else{const E=new Array;for(let k=0;k<g.children.length;k++){const W=g.children[k];if(W instanceof z){const j=W.geometry;O.assignLODInformation(a.url,j,i,e,k,M.density),E.push(j)}}return y(E)}}}return y(null)});return this.previouslyLoaded.set(u,A),await A}else if(r instanceof C){s&&console.log("Load texture from uri: "+h);const u=await new ge().loadAsync(h);return u?(u.guid=l.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=r.colorSpace,s&&console.log(l,u)):v&&console.warn("failed loading",h),u}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,o,n,s){if(!e)return;e.userData||(e.userData={});const a=new ve(r,t,o,n,s);e.userData.LODS=a,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.magFilter=r.magFilter,e.minFilter=r.minFilter,e.wrapS=r.wrapS,e.wrapT=r.wrapT,e.flipY=r.flipY,e.anisotropy=r.anisotropy,e.generateMipmaps=r.generateMipmaps,e)}};let w=O;c(w,"registerTexture",(r,e,t,o)=>{v&&console.log("> Progressive: register texture",t,e.name,e.uuid,e,o),e.source&&(e.source[J]=o);const n=o.guid;O.assignLODInformation(r,e,n,0,0,void 0),O.lodInfos.set(n,o),O.lowresCache.set(n,e)}),c(w,"registerMesh",(r,e,t,o,n,s)=>{var a;v&&console.log("> Progressive: register mesh",n,t.name,s,t.uuid,t);const i=t.geometry;i.userData||(i.userData={}),O.assignLODInformation(r,i,e,o,n,s.density),O.lodInfos.set(e,s);let l=O.lowresCache.get(e);l?l.push(t.geometry):l=[t.geometry],O.lowresCache.set(e,l),o>0&&!te(t)&&se(t,i);for(const d of R)(a=d.onRegisteredNewMesh)==null||a.call(d,t,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map),c(w,"_copiedTextures",new Map);class ve{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 Z=Y("debugprogressive"),Oe=Y("noprogressive"),ne=Symbol("Needle:LODSManager"),_=class{constructor(r){c(this,"renderer"),c(this,"projectionScreenMatrix",new ee),c(this,"cameraFrustrum",new me),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new pe),c(this,"_tempBox",new re),c(this,"tempMatrix",new ee),c(this,"_tempWorldPosition",new I),c(this,"_tempBoxSize",new I),c(this,"_tempBox2Size",new I),this.renderer=r}static getObjectLODState(r){var e;return(e=r.userData)==null?void 0:e.LOD_state}static addPlugin(r){R.push(r)}static removePlugin(r){const e=R.indexOf(r);e>=0&&R.splice(e,1)}static get(r){return r[ne]?r[ne]:new _(r)}enable(){if(this._originalRender)return;let r=0;this._originalRender=this.renderer.render;const e=this;K(this.renderer),this.renderer.render=function(t,o){e.renderer.getRenderTarget()==null&&(r=0,e._frame+=1);const n=e._frame,s=r++;e.onBeforeRender(t,o,s,n),e._originalRender.call(this,t,o),e.onAfterRender(t,o,s,n)}}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(this.pause)return;const a=this.renderer.renderLists.get(r,0),i=a.opaque;let l=!0;if(i.length===1){const d=i[0].material;(d.name==="EffectMaterial"||d.name==="CopyShader")&&(l=!1)}if(l){if(Oe||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const d=this.targetTriangleDensity;for(const u of i){if(u.material&&(((n=u.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=u.geometry)==null?void 0:s.type)==="BufferGeometry")&&(u.material.name==="SphericalGaussianBlur"||u.material.name=="BackgroundCubeMaterial"||u.material.name==="CubemapFromEquirect"||u.material.name==="EquirectangularToCubeUV")){Z&&(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",u,u.material.name,u.material.type)));continue}const p=u.object;(p instanceof z||p.isMesh)&&this.updateLODs(r,e,p,d)}const h=a.transparent;for(const u of h){const p=u.object;(p instanceof z||p.isMesh)&&this.updateLODs(r,e,p,d)}}}updateLODs(r,e,t,o){var n,s;for(const d of R)(n=d.onBeforeUpdateLOD)==null||n.call(d,this.renderer,r,e,t);let a=t.userData.LOD_state;a||(a=new we,t.userData.LOD_state=a);let i=this.calculateLodLevel(e,t,a,o);i=Math.round(i),i>=0&&this.loadProgressiveMeshes(t,i);let l=0;if(t.material){const d=t["DEBUG:LOD"];if(d!=null&&(l=d),Array.isArray(t.material))for(const h of t.material)this.loadProgressiveTextures(h,l);else this.loadProgressiveTextures(t.material,l)}for(const d of R)(s=d.onAfterUpdatedLOD)==null||s.call(d,this.renderer,r,e,t,i);a.lastLodLevel=i}loadProgressiveTextures(r,e){return r&&r.userData&&r.userData.LOD!==e?(r.userData.LOD=e,w.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 w.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(Z&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=w.getMeshLODInformation(e.geometry),i=a?.lods;if(!i||i.length<=0||!((n=this.cameraFrustrum)!=null&&n.intersectsObject(e)))return 99;const l=e.geometry.boundingBox;if(l&&r.isPerspectiveCamera){const d=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 f=r.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f))return 0}if(this._tempBox.copy(l),this._tempBox.applyMatrix4(e.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&d.fov>70){const f=this._tempBox.min,m=this._tempBox.max;let b=f.x,x=f.y,S=m.x,T=m.y;const L=2,g=1.5,M=(f.x+m.x)*.5,E=(f.y+m.y)*.5;b=(b-M)*L+M,x=(x-E)*L+E,S=(S-M)*L+M,T=(T-E)*L+E;const k=b<0&&S>0?0:Math.min(Math.abs(f.x),Math.abs(m.x)),W=x<0&&T>0?0:Math.min(Math.abs(f.y),Math.abs(m.y)),j=Math.max(k,W);t.lastCentrality=(g-j)*(g-j)*(g-j)}else t.lastCentrality=1;const h=this._tempBox.getSize(this._tempBoxSize);h.multiplyScalar(.5),screen.availHeight>0&&h.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),h.x*=d.aspect;const u=r.matrixWorldInverse,p=new re;p.copy(l),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(u);const D=p.getSize(this._tempBox2Size),A=Math.max(D.x,D.y);if(Math.max(h.x,h.y)!=0&&A!=0&&(h.z=D.z/Math.max(D.x,D.y)*Math.max(h.x,h.y)),t.lastScreenCoverage=Math.max(h.x,h.y,h.z),t.lastScreenspaceVolume.copy(h),t.lastScreenCoverage*=t.lastCentrality,Z&&_.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const m=_.corner0,b=_.corner1,x=_.corner2,S=_.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 T=(m.z+S.z)*.5;m.z=b.z=x.z=S.z=T,m.applyMatrix4(f),b.applyMatrix4(f),x.applyMatrix4(f),S.applyMatrix4(f),_.debugDrawLine(m,b,255),_.debugDrawLine(m,x,255),_.debugDrawLine(b,S,255),_.debugDrawLine(x,S,255)}let y=999;if(i&&t.lastScreenCoverage>0){for(let f=0;f<i.length;f++)if(i[f].density/t.lastScreenCoverage<o){y=f;break}}y<s&&(s=y)}}return s}};let B=_;c(B,"debugDrawLine"),c(B,"corner0",new I),c(B,"corner1",new I),c(B,"corner2",new I),c(B,"corner3",new I);class we{constructor(){c(this,"lastLodLevel",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new I),c(this,"lastCentrality",0)}}const ie=Symbol("NEEDLE_mesh_lod"),q=Symbol("NEEDLE_texture_lod");function ae(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(i=>i.toString()=="Symbol(renderer)"),a=n.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=r[s].threeRenderer),!t&&a!=null&&(t=r[a])}if(e){console.log("Adding Needle LODs to modelviewer");const o=B.get(e);if(B.addPlugin(new Me(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 Me{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[q]==!0)return;t[q]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&t.material){let s=function(i){var l,d,h;if(i[q]==!0)return;i[q]=!0,i.userData&&(i.userData.LOD=-1);const u=Object.keys(i);for(let p=0;p<u.length;p++){const D=u[p],A=i[D];if(A?.isTexture===!0){const y=(d=(l=A.userData)==null?void 0:l.associations)==null?void 0:d.textures,f=o.parser.json.textures[y];if(!f){console.warn("Texture data not found for texture index "+y);continue}if((h=f?.extensions)!=null&&h[P]){const m=f.extensions[P];m&&n&&w.registerTexture(n,A,m.lods.length,m)}}}};const a=t.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,t){var o,n;if(t[ie]==!0)return;t[ie]=!0;const s=this.getUrl();if(!s)return;const a=(n=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[P];if(a&&s){const i=t.uuid;w.registerMesh(s,i,t,0,a.lods.length,a)}}}function be(r,e,t,o){K(e),H(t),t.register(s=>new w(s,r));const n=B.get(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{ae(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,B as LODsManager,w as NEEDLE_progressive,H as addDracoAndKTX2Loaders,K as createLoaders,te as getRaycastMesh,ae as patchModelViewer,De as setDracoDecoderLocation,xe as setKTX2TranscoderLocation,se as setRaycastMesh,be as useNeedleProgressive};
@@ -1,3 +1,3 @@
1
- "use strict";var ie=Object.defineProperty;var ne=(l,e,t)=>e in l?ie(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var c=(l,e,t)=>(ne(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("three"),oe=require("three/examples/jsm/loaders/GLTFLoader.js"),ae=require("three/examples/jsm/libs/meshopt_decoder.module.js"),le=require("three/examples/jsm/loaders/DRACOLoader.js"),ue=require("three/examples/jsm/loaders/KTX2Loader.js");let V="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",J="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(V+"draco_decoder.js",{method:"head"}).catch(l=>{V="./include/draco/",J="./include/ktx2/"});function ce(l){V=l}function fe(l){J=l}let I,$,U;function Q(l){I||(I=new le.DRACOLoader,I.setDecoderPath(V),I.setDecoderConfig({type:"js"})),U||(U=new ue.KTX2Loader,U.setTranscoderPath(J)),$||($=ae.MeshoptDecoder),l?U.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Z(l){l.dracoLoader||l.setDRACOLoader(I),l.ktx2Loader||l.setKTX2Loader(U),l.meshoptDecoder||l.setMeshoptDecoder($)}function j(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function de(l,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||l===void 0)return e;const t=l.lastIndexOf("/");if(t>=0){const r=l.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}const Y=new Array;function ge(l){Y.push(l)}const C="NEEDLE_progressive",T=j("debugprogressive"),X=Symbol("needle-progressive-texture"),N=new Map,H=new Set;if(T){let l=function(){e+=1,console.log("Toggle LOD level",e,N),N.forEach((i,n)=>{for(const s of i.keys){const o=n[s];if(o.isBufferGeometry===!0){const a=M.getMeshLODInformation(o),g=a?Math.min(e,a.lods.length):0;n["DEBUG:LOD"]=e,M.assignMeshLOD(n,g),a&&(t=Math.max(t,a.lods.length-1))}else if(n.isMaterial===!0){n["DEBUG:LOD"]=e,M.assignTextureLOD(n,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,H&&H.forEach(n=>{n.name!="BackgroundCubeMaterial"&&"wireframe"in n&&(n.wireframe=r)}))})}function ee(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){c(this,"parser");c(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 n;if(e.isMaterial===!0){for(const s of Object.keys(e)){const o=e[s];if(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 h.Mesh||e.isMesh===!0){const i=e.geometry,n=this.getAssignedLODInformation(i);if(!n)return Promise.resolve(null);for(const s of Y)(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 o=n.index||0;s=s[o]}s&&i!=s&&s instanceof h.BufferGeometry&&(e.geometry=s,T&&ee(e,"geometry",n.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=[],n=new Array;if(T&&H.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const o of Object.keys(s.uniforms)){const a=s.uniforms[o].value;if((a==null?void 0:a.isTexture)===!0){const g=this.assignTextureLODForSlot(a,t,r,o);i.push(g),n.push(o)}}}else for(const s of Object.keys(r)){const o=r[s];if((o==null?void 0:o.isTexture)===!0){const a=this.assignTextureLODForSlot(o,t,r,s);i.push(a),n.push(s)}}return Promise.all(i).then(s=>{const o=new Array;for(let a=0;a<s.length;a++){const g=s[a],f=n[a];g&&g.isTexture===!0?o.push({material:r,slot:f,texture:g,level:t}):o.push({material:r,slot:f,texture:null,level:t})}return o})}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(n=>{if(Array.isArray(n))return null;if((n==null?void 0:n.isTexture)===!0){if(n!=e&&(r&&i&&(r[i]=n),T&&i&&r)){const s=this.getAssignedLODInformation(e);s&&ee(r,i,s.url)}return n}else T=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(n=>(console.error("Error loading LOD",e,n),null))}afterRoot(e){var t,r;return T&&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[C];if(s){let o=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===n&&(o=!0,S.registerTexture(this.url,a,n,s));o||this.parser.getDependency("texture",n).then(a=>{a&&S.registerTexture(this.url,a,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[C];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const a=this.parser.associations.get(o);a.meshes===n&&S.registerMesh(this.url,s.guid,o,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var o,a,g;const r=T=="verbose",i=e.userData.LODS;if(!i)return null;const n=i==null?void 0:i.key;let s;if(e.isTexture===!0){const f=e;f.source&&f.source[X]&&(s=f.source[X])}if(s||(s=S.lodInfos.get(n)),s){if(t>0){let u=!1;const p=Array.isArray(s.lods);if(p&&t>=s.lods.length?u=!0:p||(u=!0),u)return this.lowresCache.get(n)}const f=Array.isArray(s.lods)?s.lods[t].path:s.lods;if(!f)return T&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const y=de(i.url,f);if(y.endsWith(".glb")||y.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const u=y+"_"+s.guid,p=this.previouslyLoaded.get(u);if(p!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${u}`);let w=await p.catch(F=>(console.error(`Error loading LOD ${t} from ${y}
2
- `,F),null)),k=!1;if(w==null||(w instanceof h.Texture&&e instanceof h.Texture?(o=w.image)!=null&&o.data||(a=w.source)!=null&&a.data?w=this.copySettings(e,w):(k=!0,this.previouslyLoaded.delete(u)):w instanceof h.BufferGeometry&&e instanceof h.BufferGeometry&&((g=w.attributes.position)!=null&&g.array||(k=!0,this.previouslyLoaded.delete(u)))),!k)return w}const x=s,P=new Promise(async(w,k)=>{const F=new oe.GLTFLoader;Z(F),T&&(await new Promise(L=>setTimeout(L,1e3)),r&&console.warn("Start loading (delayed) "+y,x.guid));let D=y;if(x&&Array.isArray(x.lods)){const L=x.lods[t];L.hash&&(D+="?v="+L.hash)}const m=await F.loadAsync(D).catch(L=>(console.error(`Error loading LOD ${t} from ${y}
3
- `,L),null));if(!m)return null;const b=m.parser;r&&console.log("Loading finished "+y,x.guid);let O=0;if(m.parser.json.textures){let L=!1;for(const d of m.parser.json.textures){if(d!=null&&d.extensions){const v=d==null?void 0:d.extensions[C];if(v!=null&&v.guid&&v.guid===x.guid){L=!0;break}}O++}if(L){let d=await b.getDependency("texture",O);return r&&console.log('change "'+e.name+'" → "'+d.name+'"',y,O,d,u),e instanceof h.Texture&&(d=this.copySettings(e,d)),d&&(d.guid=x.guid),w(d)}}if(O=0,m.parser.json.meshes){let L=!1;for(const d of m.parser.json.meshes){if(d!=null&&d.extensions){const v=d==null?void 0:d.extensions[C];if(v!=null&&v.guid&&v.guid===x.guid){L=!0;break}}O++}if(L){const d=await b.getDependency("mesh",O),v=x;if(r&&console.log(`Loaded Mesh "${d.name}"`,y,O,d,u),d.isMesh===!0){const B=d.geometry;return S.assignLODInformation(i.url,B,n,t,void 0,v.density),w(B)}else{const B=new Array;for(let E=0;E<d.children.length;E++){const R=d.children[E];if(R instanceof h.Mesh){const W=R.geometry;S.assignLODInformation(i.url,W,n,t,E,v.density),B.push(W)}}return w(B)}}}return w(null)});return this.previouslyLoaded.set(u,P),await P}else if(e instanceof h.Texture){r&&console.log("Load texture from uri: "+y);const p=await new h.TextureLoader().loadAsync(y);return p?(p.guid=s.guid,p.flipY=!1,p.needsUpdate=!0,p.colorSpace=e.colorSpace,r&&console.log(s,p)):T&&console.warn("failed loading",y),p}}else T&&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 he(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){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.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.generateMipmaps=e.generateMipmaps,t)}};let M=S;c(M,"registerTexture",(e,t,r,i)=>{T&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[X]=i);const n=i.guid;S.assignLODInformation(e,t,n,0,0,void 0),S.lodInfos.set(n,i),S.lowresCache.set(n,t)}),c(M,"registerMesh",(e,t,r,i,n,s)=>{var g;T&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),S.assignLODInformation(e,o,t,i,n,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 f of Y)(g=f.onRegisteredNewMesh)==null||g.call(f,r,s)}),c(M,"lodInfos",new Map),c(M,"previouslyLoaded",new Map),c(M,"lowresCache",new Map),c(M,"_copiedTextures",new Map);class he{constructor(e,t,r,i,n){c(this,"url");c(this,"key");c(this,"level");c(this,"index");c(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),n!=null&&(this.density=n)}}const K=j("debugprogressive"),pe=j("noprogressive"),A=class{constructor(e){c(this,"renderer");c(this,"projectionScreenMatrix",new h.Matrix4);c(this,"cameraFrustrum",new h.Frustum);c(this,"updateInterval",0);c(this,"pause",!1);c(this,"plugins",[]);c(this,"_frame",0);c(this,"_originalRender");c(this,"_sphere",new h.Sphere);c(this,"_tempBox",new h.Box3);c(this,"tempMatrix",new h.Matrix4);c(this,"_tempWorldPosition",new h.Vector3);c(this,"_tempBoxSize",new h.Vector3);c(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;Q(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 a,g;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),s=n.opaque;let o=!0;if(s.length===1){const f=s[0].material;(f.name==="EffectMaterial"||f.name==="CopyShader")&&(o=!1)}if(o){if(pe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const f=1e5;for(const u of s){if(u.material&&(((a=u.geometry)==null?void 0:a.type)==="BoxGeometry"||((g=u.geometry)==null?void 0:g.type)==="BufferGeometry")&&(u.material.name==="SphericalGaussianBlur"||u.material.name=="BackgroundCubeMaterial"||u.material.name==="CubemapFromEquirect"||u.material.name==="EquirectangularToCubeUV")){K&&(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",u,u.material.name,u.material.type)));continue}const p=u.object;(p instanceof h.Mesh||p.isMesh)&&this.updateLODs(e,t,p,f)}const y=n.transparent;for(const u of y){const p=u.object;(p instanceof h.Mesh||p.isMesh)&&this.updateLODs(e,t,p,f)}}}updateLODs(e,t,r,i){var a,g;for(const f of this.plugins)(a=f.onBeforeUpdateLOD)==null||a.call(f,this.renderer,e,t,r);let n=r.userData.LOD_state;n||(n=new ye,r.userData.LOD_state=n);let s=this.calculateLodLevel(t,r,n,i);s=Math.round(s),s>=0&&this.loadProgressiveMeshes(r,s);let o=0;if(r.material){const f=r["DEBUG:LOD"];if(f!=null&&(o=f),Array.isArray(r.material))for(const y of r.material)this.loadProgressiveTextures(y,o);else this.loadProgressiveTextures(r.material,o)}for(const f of this.plugins)(g=f.onAfterUpdatedLOD)==null||g.call(f,this.renderer,e,t,r,s);n.lastLodLevel=s}loadProgressiveTextures(e,t){return e&&e.userData&&e.userData.LOD!==t?(e.userData.LOD=t,M.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 M.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}calculateLodLevel(e,t,r,i){var o;if(!t)return-1;let s=10+1;if(e){if(K&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const a=M.getMeshLODInformation(t.geometry),g=a==null?void 0:a.lods;if(!g||g.length<=0||!((o=this.cameraFrustrum)!=null&&o.intersectsObject(t)))return 99;const f=t.geometry.boundingBox;if(f&&e.isPerspectiveCamera){const y=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 D=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(D))return 0}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(t.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&y.fov>70){const D=this._tempBox.min,m=this._tempBox.max;let b=D.x,O=D.y,L=m.x,d=m.y;const v=2,B=1.5,E=(D.x+m.x)*.5,R=(D.y+m.y)*.5;b=(b-E)*v+E,O=(O-R)*v+R,L=(L-E)*v+E,d=(d-R)*v+R;const W=b<0&&L>0?0:Math.min(Math.abs(D.x),Math.abs(m.x)),se=O<0&&d>0?0:Math.min(Math.abs(D.y),Math.abs(m.y)),q=Math.max(W,se);r.lastCentrality=(B-q)*(B-q)*(B-q)}else r.lastCentrality=1;const u=this._tempBox.getSize(this._tempBoxSize);u.multiplyScalar(.5),screen.availHeight>0&&u.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),u.x*=y.aspect;const p=e.matrixWorldInverse,x=new h.Box3;x.copy(f),x.applyMatrix4(t.matrixWorld),x.applyMatrix4(p);const P=x.getSize(this._tempBox2Size),G=Math.max(P.x,P.y);if(Math.max(u.x,u.y)!=0&&G!=0&&(u.z=P.z/Math.max(P.x,P.y)*Math.max(u.x,u.y)),r.lastScreenCoverage=Math.max(u.x,u.y,u.z),r.lastScreenspaceVolume.copy(u),r.lastScreenCoverage*=r.lastCentrality,K&&A.debugDrawLine){const D=this.tempMatrix.copy(this.projectionScreenMatrix);D.invert();const m=A.corner0,b=A.corner1,O=A.corner2,L=A.corner3;m.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=m.x,O.copy(this._tempBox.max),O.y=m.y,L.copy(this._tempBox.max);const d=(m.z+L.z)*.5;m.z=b.z=O.z=L.z=d,m.applyMatrix4(D),b.applyMatrix4(D),O.applyMatrix4(D),L.applyMatrix4(D),A.debugDrawLine(m,b,255),A.debugDrawLine(m,O,255),A.debugDrawLine(b,L,255),A.debugDrawLine(O,L,255)}let k=999;if(g&&r.lastScreenCoverage>0){for(let D=0;D<g.length;D++)if(g[D].density/r.lastScreenCoverage<i){k=D;break}}k<s&&(s=k)}}return s}};let _=A;c(_,"debugDrawLine"),c(_,"corner0",new h.Vector3),c(_,"corner1",new h.Vector3),c(_,"corner2",new h.Vector3),c(_,"corner3",new h.Vector3);class ye{constructor(){c(this,"lastLodLevel",0);c(this,"lastScreenCoverage",0);c(this,"lastScreenspaceVolume",new h.Vector3);c(this,"lastCentrality",0)}}const te=Symbol("NEEDLE_mesh_lod"),z=Symbol("NEEDLE_texture_lod");function re(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),n=i.find(o=>o.toString()=="Symbol(renderer)"),s=i.find(o=>o.toString()=="Symbol(scene)");!e&&n!=null&&(e=l[n].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 me(l)),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 me{constructor(e){c(this,"modelviewer");c(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[z]==!0)return;t[z]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let n=function(o){var g,f,y;if(o[z]==!0)return;o[z]=!0,o.userData&&(o.userData.LOD=-1);const a=Object.keys(o);for(let u=0;u<a.length;u++){const p=a[u],x=o[p];if((x==null?void 0:x.isTexture)===!0){const P=(f=(g=x.userData)==null?void 0:g.associations)==null?void 0:f.textures,G=r.parser.json.textures[P];if((y=G.extensions)!=null&&y[C]){const w=G.extensions[C];w&&i&&M.registerTexture(i,x,w.lods.length,w)}}}};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[te]==!0)return;t[te]=!0;const r=this.getUrl();if(!r)return;const i=(s=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[C];if(i&&r){const o=t.uuid;M.registerMesh(r,o,t,0,i.lods.length,i)}}}function Le(l,e,t,r){Q(e),Z(t),t.register(n=>new M(n,l));const i=new _(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{re(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=C;exports.LODsManager=_;exports.NEEDLE_progressive=M;exports.addDracoAndKTX2Loaders=Z;exports.createLoaders=Q;exports.patchModelViewer=re;exports.registerPlugin=ge;exports.setDracoDecoderLocation=ce;exports.setKTX2TranscoderLocation=fe;exports.useNeedleProgressive=Le;
1
+ "use strict";var ae=Object.defineProperty;var le=(a,e,t)=>e in a?ae(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var u=(a,e,t)=>(le(a,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ce=require("three/examples/jsm/libs/meshopt_decoder.module.js"),ue=require("three/examples/jsm/loaders/DRACOLoader.js"),fe=require("three/examples/jsm/loaders/KTX2Loader.js"),h=require("three"),de=require("three/examples/jsm/loaders/GLTFLoader.js");let q="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",J="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(q+"draco_decoder.js",{method:"head"}).catch(a=>{q="./include/draco/",J="./include/ktx2/"});function ge(a){q=a}function he(a){J=a}let N,Y,U;function Q(a){N||(N=new ue.DRACOLoader,N.setDecoderPath(q),N.setDecoderConfig({type:"js"})),U||(U=new fe.KTX2Loader,U.setTranscoderPath(J)),Y||(Y=ce.MeshoptDecoder),a?U.detectSupport(a):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Z(a){a.dracoLoader||a.setDRACOLoader(N),a.ktx2Loader||a.setKTX2Loader(U),a.meshoptDecoder||a.setMeshoptDecoder(Y)}function j(a){const t=new URL(window.location.href).searchParams.get(a);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function pe(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 se(a){var e;return((e=a.userData)==null?void 0:e["needle:raycast-mesh"])instanceof h.BufferGeometry?a.userData["needle:raycast-mesh"]:null}function ie(a,e){(a.type==="Mesh"||a.type==="SkinnedMesh")&&(a.userData||(a.userData={}),a.userData["needle:raycast-mesh"]=e)}const F=new Array,R="NEEDLE_progressive",T=j("debugprogressive"),$=Symbol("needle-progressive-texture"),W=new Map,H=new Set;if(T){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=M.getMeshLODInformation(o),g=l?Math.min(e,l.lods.length):0;n["DEBUG:LOD"]=e,M.assignMeshLOD(n,g),l&&(t=Math.max(t,l.lods.length-1))}else if(n.isMaterial===!0){n["DEBUG:LOD"]=e,M.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,H&&H.forEach(n=>{n.name!="BackgroundCubeMaterial"&&"wireframe"in n&&(n.wireframe=r)}))})}function ee(a,e,t){var i;if(!T)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 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 R}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static hasLODLevelAvailable(e,t){var n;if(e.isMaterial===!0){for(const s of Object.keys(e)){const o=e[s];if(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 h.Mesh||e.isMesh===!0){const i=e.geometry,n=this.getAssignedLODInformation(i);if(!n)return Promise.resolve(null);for(const s of F)(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 o=n.index||0;s=s[o]}s&&i!=s&&s instanceof h.BufferGeometry&&(e.geometry=s,T&&ee(e,"geometry",n.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=[],n=new Array;if(T&&H.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 g=this.assignTextureLODForSlot(l,t,r,o);i.push(g),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 g=s[l],f=n[l];g&&g.isTexture===!0?o.push({material:r,slot:f,texture:g,level:t}):o.push({material:r,slot:f,texture:null,level:t})}return o})}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(n=>{if(Array.isArray(n))return null;if((n==null?void 0:n.isTexture)===!0){if(n!=e&&(r&&i&&(r[i]=n),T&&i&&r)){const s=this.getAssignedLODInformation(e);s&&ee(r,i,s.url)}return n}else T=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(n=>(console.error("Error loading LOD",e,n),null))}afterRoot(e){var t,r;return T&&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[R];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,S.registerTexture(this.url,l,n,s));o||this.parser.getDependency("texture",n).then(l=>{l&&S.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[R];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&&S.registerMesh(this.url,s.guid,o,s.lods.length,l.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var o,l,g;const r=T=="verbose",i=e.userData.LODS;if(!i)return null;const n=i==null?void 0:i.key;let s;if(e.isTexture===!0){const f=e;f.source&&f.source[$]&&(s=f.source[$])}if(s||(s=S.lodInfos.get(n)),s){if(t>0){let c=!1;const p=Array.isArray(s.lods);if(p&&t>=s.lods.length?c=!0:p||(c=!0),c)return this.lowresCache.get(n)}const f=Array.isArray(s.lods)?s.lods[t].path:s.lods;if(!f)return T&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const y=pe(i.url,f);if(y.endsWith(".glb")||y.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const c=y+"_"+s.guid,p=this.previouslyLoaded.get(c);if(p!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${c}`);let O=await p.catch(I=>(console.error(`Error loading LOD ${t} from ${y}
2
+ `,I),null)),C=!1;if(O==null||(O instanceof h.Texture&&e instanceof h.Texture?(o=O.image)!=null&&o.data||(l=O.source)!=null&&l.data?O=this.copySettings(e,O):(C=!0,this.previouslyLoaded.delete(c)):O instanceof h.BufferGeometry&&e instanceof h.BufferGeometry&&((g=O.attributes.position)!=null&&g.array||(C=!0,this.previouslyLoaded.delete(c)))),!C)return O}const x=s,P=new Promise(async(O,C)=>{const I=new de.GLTFLoader;Z(I),T&&(await new Promise(L=>setTimeout(L,1e3)),r&&console.warn("Start loading (delayed) "+y,x.guid));let D=y;if(x&&Array.isArray(x.lods)){const L=x.lods[t];L.hash&&(D+="?v="+L.hash)}const m=await I.loadAsync(D).catch(L=>(console.error(`Error loading LOD ${t} from ${y}
3
+ `,L),null));if(!m)return null;const A=m.parser;r&&console.log("Loading finished "+y,x.guid);let w=0;if(m.parser.json.textures){let L=!1;for(const d of m.parser.json.textures){if(d!=null&&d.extensions){const v=d==null?void 0:d.extensions[R];if(v!=null&&v.guid&&v.guid===x.guid){L=!0;break}}w++}if(L){let d=await A.getDependency("texture",w);return r&&console.log('change "'+e.name+'" → "'+d.name+'"',y,w,d,c),e instanceof h.Texture&&(d=this.copySettings(e,d)),d&&(d.guid=x.guid),O(d)}}if(w=0,m.parser.json.meshes){let L=!1;for(const d of m.parser.json.meshes){if(d!=null&&d.extensions){const v=d==null?void 0:d.extensions[R];if(v!=null&&v.guid&&v.guid===x.guid){L=!0;break}}w++}if(L){const d=await A.getDependency("mesh",w),v=x;if(r&&console.log(`Loaded Mesh "${d.name}"`,y,w,d,c),d.isMesh===!0){const B=d.geometry;return S.assignLODInformation(i.url,B,n,t,void 0,v.density),O(B)}else{const B=new Array;for(let E=0;E<d.children.length;E++){const G=d.children[E];if(G instanceof h.Mesh){const z=G.geometry;S.assignLODInformation(i.url,z,n,t,E,v.density),B.push(z)}}return O(B)}}}return O(null)});return this.previouslyLoaded.set(c,P),await P}else if(e instanceof h.Texture){r&&console.log("Load texture from uri: "+y);const p=await new h.TextureLoader().loadAsync(y);return p?(p.guid=s.guid,p.flipY=!1,p.needsUpdate=!0,p.colorSpace=e.colorSpace,r&&console.log(s,p)):T&&console.warn("failed loading",y),p}}else T&&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 ye(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){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.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.generateMipmaps=e.generateMipmaps,t)}};let M=S;u(M,"registerTexture",(e,t,r,i)=>{T&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[$]=i);const n=i.guid;S.assignLODInformation(e,t,n,0,0,void 0),S.lodInfos.set(n,i),S.lowresCache.set(n,t)}),u(M,"registerMesh",(e,t,r,i,n,s)=>{var g;T&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),S.assignLODInformation(e,o,t,i,n,s.density),S.lodInfos.set(t,s);let l=S.lowresCache.get(t);l?l.push(r.geometry):l=[r.geometry],S.lowresCache.set(t,l),i>0&&!se(r)&&ie(r,o);for(const f of F)(g=f.onRegisteredNewMesh)==null||g.call(f,r,s)}),u(M,"lodInfos",new Map),u(M,"previouslyLoaded",new Map),u(M,"lowresCache",new Map),u(M,"_copiedTextures",new Map);class ye{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 K=j("debugprogressive"),me=j("noprogressive"),te=Symbol("Needle:LODSManager"),b=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new h.Matrix4);u(this,"cameraFrustrum",new h.Frustum);u(this,"targetTriangleDensity",2e5);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"_frame",0);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}static addPlugin(e){F.push(e)}static removePlugin(e){const t=F.indexOf(e);t>=0&&F.splice(t,1)}static get(e){return e[te]?e[te]:new b(e)}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;Q(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,g;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),s=n.opaque;let o=!0;if(s.length===1){const f=s[0].material;(f.name==="EffectMaterial"||f.name==="CopyShader")&&(o=!1)}if(o){if(me||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const f=this.targetTriangleDensity;for(const c of s){if(c.material&&(((l=c.geometry)==null?void 0:l.type)==="BoxGeometry"||((g=c.geometry)==null?void 0:g.type)==="BufferGeometry")&&(c.material.name==="SphericalGaussianBlur"||c.material.name=="BackgroundCubeMaterial"||c.material.name==="CubemapFromEquirect"||c.material.name==="EquirectangularToCubeUV")){K&&(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 p=c.object;(p instanceof h.Mesh||p.isMesh)&&this.updateLODs(e,t,p,f)}const y=n.transparent;for(const c of y){const p=c.object;(p instanceof h.Mesh||p.isMesh)&&this.updateLODs(e,t,p,f)}}}updateLODs(e,t,r,i){var l,g;for(const f of F)(l=f.onBeforeUpdateLOD)==null||l.call(f,this.renderer,e,t,r);let n=r.userData.LOD_state;n||(n=new Le,r.userData.LOD_state=n);let s=this.calculateLodLevel(t,r,n,i);s=Math.round(s),s>=0&&this.loadProgressiveMeshes(r,s);let o=0;if(r.material){const f=r["DEBUG:LOD"];if(f!=null&&(o=f),Array.isArray(r.material))for(const y of r.material)this.loadProgressiveTextures(y,o);else this.loadProgressiveTextures(r.material,o)}for(const f of F)(g=f.onAfterUpdatedLOD)==null||g.call(f,this.renderer,e,t,r,s);n.lastLodLevel=s}loadProgressiveTextures(e,t){return e&&e.userData&&e.userData.LOD!==t?(e.userData.LOD=t,M.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 M.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}calculateLodLevel(e,t,r,i){var o;if(!t)return-1;let s=10+1;if(e){if(K&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const l=M.getMeshLODInformation(t.geometry),g=l==null?void 0:l.lods;if(!g||g.length<=0||!((o=this.cameraFrustrum)!=null&&o.intersectsObject(t)))return 99;const f=t.geometry.boundingBox;if(f&&e.isPerspectiveCamera){const y=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 D=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(D))return 0}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(t.matrixWorld),this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&y.fov>70){const D=this._tempBox.min,m=this._tempBox.max;let A=D.x,w=D.y,L=m.x,d=m.y;const v=2,B=1.5,E=(D.x+m.x)*.5,G=(D.y+m.y)*.5;A=(A-E)*v+E,w=(w-G)*v+G,L=(L-E)*v+E,d=(d-G)*v+G;const z=A<0&&L>0?0:Math.min(Math.abs(D.x),Math.abs(m.x)),oe=w<0&&d>0?0:Math.min(Math.abs(D.y),Math.abs(m.y)),X=Math.max(z,oe);r.lastCentrality=(B-X)*(B-X)*(B-X)}else r.lastCentrality=1;const c=this._tempBox.getSize(this._tempBoxSize);c.multiplyScalar(.5),screen.availHeight>0&&c.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),c.x*=y.aspect;const p=e.matrixWorldInverse,x=new h.Box3;x.copy(f),x.applyMatrix4(t.matrixWorld),x.applyMatrix4(p);const P=x.getSize(this._tempBox2Size),k=Math.max(P.x,P.y);if(Math.max(c.x,c.y)!=0&&k!=0&&(c.z=P.z/Math.max(P.x,P.y)*Math.max(c.x,c.y)),r.lastScreenCoverage=Math.max(c.x,c.y,c.z),r.lastScreenspaceVolume.copy(c),r.lastScreenCoverage*=r.lastCentrality,K&&b.debugDrawLine){const D=this.tempMatrix.copy(this.projectionScreenMatrix);D.invert();const m=b.corner0,A=b.corner1,w=b.corner2,L=b.corner3;m.copy(this._tempBox.min),A.copy(this._tempBox.max),A.x=m.x,w.copy(this._tempBox.max),w.y=m.y,L.copy(this._tempBox.max);const d=(m.z+L.z)*.5;m.z=A.z=w.z=L.z=d,m.applyMatrix4(D),A.applyMatrix4(D),w.applyMatrix4(D),L.applyMatrix4(D),b.debugDrawLine(m,A,255),b.debugDrawLine(m,w,255),b.debugDrawLine(A,L,255),b.debugDrawLine(w,L,255)}let C=999;if(g&&r.lastScreenCoverage>0){for(let D=0;D<g.length;D++)if(g[D].density/r.lastScreenCoverage<i){C=D;break}}C<s&&(s=C)}}return s}};let _=b;u(_,"debugDrawLine"),u(_,"corner0",new h.Vector3),u(_,"corner1",new h.Vector3),u(_,"corner2",new h.Vector3),u(_,"corner3",new h.Vector3);class Le{constructor(){u(this,"lastLodLevel",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new h.Vector3);u(this,"lastCentrality",0)}}const re=Symbol("NEEDLE_mesh_lod"),V=Symbol("NEEDLE_texture_lod");function ne(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=_.get(e);if(_.addPlugin(new De(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 De{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[V]==!0)return;t[V]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let n=function(o){var g,f,y;if(o[V]==!0)return;o[V]=!0,o.userData&&(o.userData.LOD=-1);const l=Object.keys(o);for(let c=0;c<l.length;c++){const p=l[c],x=o[p];if((x==null?void 0:x.isTexture)===!0){const P=(f=(g=x.userData)==null?void 0:g.associations)==null?void 0:f.textures,k=r.parser.json.textures[P];if(!k){console.warn("Texture data not found for texture index "+P);continue}if((y=k==null?void 0:k.extensions)!=null&&y[R]){const O=k.extensions[R];O&&i&&M.registerTexture(i,x,O.lods.length,O)}}}};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[re]==!0)return;t[re]=!0;const r=this.getUrl();if(!r)return;const i=(s=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[R];if(i&&r){const o=t.uuid;M.registerMesh(r,o,t,0,i.lods.length,i)}}}function xe(a,e,t,r){Q(e),Z(t),t.register(n=>new M(n,a));const i=_.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ne(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=R;exports.LODsManager=_;exports.NEEDLE_progressive=M;exports.addDracoAndKTX2Loaders=Z;exports.createLoaders=Q;exports.getRaycastMesh=se;exports.patchModelViewer=ne;exports.setDracoDecoderLocation=ge;exports.setKTX2TranscoderLocation=he;exports.setRaycastMesh=ie;exports.useNeedleProgressive=xe;
package/lib/extension.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BufferGeometry, Material, Mesh, Texture, TextureLoader } from "three";
2
2
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { addDracoAndKTX2Loaders } from "./loaders.js";
4
- import { getParam, 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";
@@ -394,6 +394,9 @@ export class NEEDLE_progressive {
394
394
  else
395
395
  existing = [mesh.geometry];
396
396
  NEEDLE_progressive.lowresCache.set(key, existing);
397
+ if (level > 0 && !getRaycastMesh(mesh)) {
398
+ setRaycastMesh(mesh, geometry);
399
+ }
397
400
  for (const plugin of plugins) {
398
401
  plugin.onRegisteredNewMesh?.(mesh, ext);
399
402
  }
package/lib/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
+ import { WebGLRenderer } from "three";
2
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ import { LODsManager } from "./lods_manager.js";
1
4
  export * from "./extension.js";
2
5
  export * from "./plugins/index.js";
3
6
  export { LODsManager } from "./lods_manager.js";
4
7
  export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
5
- import { WebGLRenderer } from "three";
6
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
7
- import { LODsManager } from "./lods_manager.js";
8
+ export { getRaycastMesh, setRaycastMesh } from "./utils.js";
8
9
  declare class UseNeedleGLTFProgressive {
9
- /** */
10
10
  enableLODsManager?: boolean;
11
11
  }
12
12
  /** Use this function to enable progressive loading of gltf models.
package/lib/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { addDracoAndKTX2Loaders, createLoaders } from "./loaders.js";
2
+ import { NEEDLE_progressive } from "./extension.js";
3
+ import { LODsManager } from "./lods_manager.js";
1
4
  export * from "./extension.js";
2
5
  export * from "./plugins/index.js";
3
6
  export { LODsManager } from "./lods_manager.js";
4
7
  export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
5
- import { addDracoAndKTX2Loaders, createLoaders } from "./loaders.js";
6
- import { NEEDLE_progressive } from "./extension.js";
7
- import { LODsManager } from "./lods_manager.js";
8
+ export { getRaycastMesh, setRaycastMesh } from "./utils.js";
8
9
  ;
9
10
  /** Use this function to enable progressive loading of gltf models.
10
11
  * @param url The url of the gltf model.
@@ -25,7 +26,7 @@ export function useNeedleProgressive(url, renderer, loader, opts) {
25
26
  createLoaders(renderer);
26
27
  addDracoAndKTX2Loaders(loader);
27
28
  loader.register(p => new NEEDLE_progressive(p, url));
28
- const lod = new LODsManager(renderer);
29
+ const lod = LODsManager.get(renderer);
29
30
  if (opts?.enableLODsManager !== false) {
30
31
  lod.enable();
31
32
  }
@@ -5,6 +5,29 @@ import { NEEDLE_progressive_plugin } from "./plugins/plugin.js";
5
5
  * It must be enabled by calling the `enable` method.
6
6
  *
7
7
  * Instead of using the LODs manager directly you can also call `useNeedleProgressive` to enable progressive loading for a GLTFLoader
8
+ *
9
+ * ### Plugins
10
+ * Use {@link LODsManager.addPlugin} to add a plugin to the LODsManager. A plugin can be used to hook into the LOD update process and modify the LOD levels or perform other actions.
11
+ *
12
+ * @example Adding a LODsManager to a Three.js scene:
13
+ * ```ts
14
+ * import { LODsManager } from "@needle-tools/gltf-progressive";
15
+ * import { WebGLRenderer, Scene, Camera, Mesh } from "three";
16
+ *
17
+ * const renderer = new WebGLRenderer();
18
+ * const lodsManager = LODsManager.get(renderer);
19
+ * lodsManager.enable();
20
+ * ```
21
+ *
22
+ * @example Using the LODsManager with a GLTFLoader:
23
+ * ```ts
24
+ * import { useNeedleProgressive } from "@needle-tools/gltf-progressive";
25
+ * import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
26
+ *
27
+ * const url = 'https://yourdomain.com/yourmodel.glb';
28
+ * const loader = new GLTFLoader();
29
+ * const lodsManager = useNeedleProgressive(url, renderer, loader);
30
+ * ```
8
31
  */
9
32
  export declare class LODsManager {
10
33
  /** Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
@@ -12,9 +35,22 @@ export declare class LODsManager {
12
35
  static debugDrawLine?: (a: Vector3, b: Vector3, color: number) => void;
13
36
  /** @internal */
14
37
  static getObjectLODState(object: Object3D): LOD_state | undefined;
38
+ static addPlugin(plugin: NEEDLE_progressive_plugin): void;
39
+ static removePlugin(plugin: NEEDLE_progressive_plugin): void;
40
+ /**
41
+ * Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
42
+ * @param renderer The renderer to get the LODsManager for.
43
+ * @returns The LODsManager instance.
44
+ */
45
+ static get(renderer: WebGLRenderer): LODsManager;
15
46
  readonly renderer: WebGLRenderer;
16
47
  readonly projectionScreenMatrix: Matrix4;
17
48
  readonly cameraFrustrum: Frustum;
49
+ /**
50
+ * The target triangle density is the desired max amount of triangles on screen when the mesh is filling the screen.
51
+ * @default 200_000
52
+ */
53
+ targetTriangleDensity: number;
18
54
  /**
19
55
  * The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 1, the LODs will be updated every second frame, etc.
20
56
  */
@@ -23,8 +59,7 @@ export declare class LODsManager {
23
59
  * If set to true, the LODsManager will not update the LODs.
24
60
  */
25
61
  pause: boolean;
26
- readonly plugins: NEEDLE_progressive_plugin[];
27
- constructor(renderer: WebGLRenderer);
62
+ private constructor();
28
63
  private _frame;
29
64
  private _originalRender?;
30
65
  /**
@@ -2,13 +2,38 @@ import { Box3, Frustum, Matrix4, Mesh, Sphere, Vector3 } from "three";
2
2
  import { NEEDLE_progressive } from "./extension.js";
3
3
  import { createLoaders } from "./loaders.js";
4
4
  import { getParam } from "./utils.js";
5
+ import { plugins } from "./plugins/plugin.js";
5
6
  const debugProgressiveLoading = getParam("debugprogressive");
6
7
  const suppressProgressiveLoading = getParam("noprogressive");
8
+ const $lodsManager = Symbol("Needle:LODSManager");
7
9
  /**
8
10
  * The LODsManager class is responsible for managing the LODs and progressive assets in the scene. It will automatically update the LODs based on the camera position, screen coverage and mesh density of the objects.
9
11
  * It must be enabled by calling the `enable` method.
10
12
  *
11
13
  * Instead of using the LODs manager directly you can also call `useNeedleProgressive` to enable progressive loading for a GLTFLoader
14
+ *
15
+ * ### Plugins
16
+ * Use {@link LODsManager.addPlugin} to add a plugin to the LODsManager. A plugin can be used to hook into the LOD update process and modify the LOD levels or perform other actions.
17
+ *
18
+ * @example Adding a LODsManager to a Three.js scene:
19
+ * ```ts
20
+ * import { LODsManager } from "@needle-tools/gltf-progressive";
21
+ * import { WebGLRenderer, Scene, Camera, Mesh } from "three";
22
+ *
23
+ * const renderer = new WebGLRenderer();
24
+ * const lodsManager = LODsManager.get(renderer);
25
+ * lodsManager.enable();
26
+ * ```
27
+ *
28
+ * @example Using the LODsManager with a GLTFLoader:
29
+ * ```ts
30
+ * import { useNeedleProgressive } from "@needle-tools/gltf-progressive";
31
+ * import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
32
+ *
33
+ * const url = 'https://yourdomain.com/yourmodel.glb';
34
+ * const loader = new GLTFLoader();
35
+ * const lodsManager = useNeedleProgressive(url, renderer, loader);
36
+ * ```
12
37
  */
13
38
  export class LODsManager {
14
39
  /** Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
@@ -18,9 +43,34 @@ export class LODsManager {
18
43
  static getObjectLODState(object) {
19
44
  return object.userData?.LOD_state;
20
45
  }
46
+ static addPlugin(plugin) {
47
+ plugins.push(plugin);
48
+ }
49
+ static removePlugin(plugin) {
50
+ const index = plugins.indexOf(plugin);
51
+ if (index >= 0)
52
+ plugins.splice(index, 1);
53
+ }
54
+ /**
55
+ * Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
56
+ * @param renderer The renderer to get the LODsManager for.
57
+ * @returns The LODsManager instance.
58
+ */
59
+ static get(renderer) {
60
+ if (renderer[$lodsManager]) {
61
+ return renderer[$lodsManager];
62
+ }
63
+ const lodsManager = new LODsManager(renderer);
64
+ return lodsManager;
65
+ }
21
66
  renderer;
22
67
  projectionScreenMatrix = new Matrix4();
23
68
  cameraFrustrum = new Frustum();
69
+ /**
70
+ * The target triangle density is the desired max amount of triangles on screen when the mesh is filling the screen.
71
+ * @default 200_000
72
+ */
73
+ targetTriangleDensity = 200_000;
24
74
  /**
25
75
  * The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 1, the LODs will be updated every second frame, etc.
26
76
  */
@@ -29,7 +79,7 @@ export class LODsManager {
29
79
  * If set to true, the LODsManager will not update the LODs.
30
80
  */
31
81
  pause = false;
32
- plugins = [];
82
+ // readonly plugins: NEEDLE_progressive_plugin[] = [];
33
83
  constructor(renderer) {
34
84
  this.renderer = renderer;
35
85
  }
@@ -95,7 +145,7 @@ export class LODsManager {
95
145
  return;
96
146
  this.projectionScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
97
147
  this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
98
- const desiredDensity = 100_000;
148
+ const desiredDensity = this.targetTriangleDensity;
99
149
  // const isLowPerformanceDevice = false;// isMobileDevice();
100
150
  // Experiment: quick & dirty performance-adaptive LODs
101
151
  /*
@@ -135,7 +185,7 @@ export class LODsManager {
135
185
  }
136
186
  /** Update the LOD levels for the renderer. */
137
187
  updateLODs(scene, camera, object, desiredDensity) {
138
- for (const plugin of this.plugins) {
188
+ for (const plugin of plugins) {
139
189
  plugin.onBeforeUpdateLOD?.(this.renderer, scene, camera, object);
140
190
  }
141
191
  let state = object.userData.LOD_state;
@@ -164,7 +214,7 @@ export class LODsManager {
164
214
  this.loadProgressiveTextures(object.material, textureLOD);
165
215
  }
166
216
  }
167
- for (const plugin of this.plugins) {
217
+ for (const plugin of plugins) {
168
218
  plugin.onAfterUpdatedLOD?.(this.renderer, scene, camera, object, level);
169
219
  }
170
220
  state.lastLodLevel = level;
@@ -1,2 +1,2 @@
1
1
  export { patchModelViewer } from "./modelviewer.js";
2
- export { registerPlugin, type NEEDLE_progressive_plugin } from "./plugin.js";
2
+ export { type NEEDLE_progressive_plugin } from "./plugin.js";
@@ -1,2 +1 @@
1
1
  export { patchModelViewer } from "./modelviewer.js";
2
- export { registerPlugin } from "./plugin.js";
@@ -23,8 +23,8 @@ export function patchModelViewer(modelviewer) {
23
23
  }
24
24
  if (renderer) {
25
25
  console.log("Adding Needle LODs to modelviewer");
26
- const lod = new LODsManager(renderer);
27
- lod.plugins.push(new RegisterModelviewerDataPlugin(modelviewer));
26
+ const lod = LODsManager.get(renderer);
27
+ LODsManager.addPlugin(new RegisterModelviewerDataPlugin(modelviewer));
28
28
  lod.enable();
29
29
  if (scene) {
30
30
  const camera = scene["camera"] || scene.traverse((o) => o.type == "PerspectiveCamera")[0];
@@ -104,7 +104,11 @@ class RegisterModelviewerDataPlugin {
104
104
  if (value?.isTexture === true) {
105
105
  const textureIndex = value.userData?.associations?.textures;
106
106
  const textureData = currentGLTF.parser.json.textures[textureIndex];
107
- if (textureData.extensions?.[EXTENSION_NAME]) {
107
+ if (!textureData) {
108
+ console.warn("Texture data not found for texture index " + textureIndex);
109
+ continue;
110
+ }
111
+ if (textureData?.extensions?.[EXTENSION_NAME]) {
108
112
  const ext = textureData.extensions[EXTENSION_NAME];
109
113
  if (ext && url) {
110
114
  NEEDLE_progressive.registerTexture(url, value, ext.lods.length, ext);
@@ -18,7 +18,3 @@ export interface NEEDLE_progressive_plugin {
18
18
  * @internal
19
19
  */
20
20
  export declare const plugins: NEEDLE_progressive_plugin[];
21
- /**
22
- * Register a plugin for the progressive extension. The plugin callbacks will be called at different stages of the progressive extension.
23
- */
24
- export declare function registerPlugin(plugin: NEEDLE_progressive_plugin): void;
@@ -3,9 +3,3 @@
3
3
  * @internal
4
4
  */
5
5
  export const plugins = new Array();
6
- /**
7
- * Register a plugin for the progressive extension. The plugin callbacks will be called at different stages of the progressive extension.
8
- */
9
- export function registerPlugin(plugin) {
10
- plugins.push(plugin);
11
- }
package/lib/utils.d.ts CHANGED
@@ -1,2 +1,15 @@
1
+ import { BufferGeometry, Object3D } from "three";
1
2
  export declare function getParam(name: string): boolean | string;
2
3
  export declare function resolveUrl(source: string | undefined, uri: string): string;
4
+ /**
5
+ * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
6
+ * @param obj the object to get the raycast mesh from
7
+ * @returns the raycast mesh or null if not set
8
+ */
9
+ export declare function getRaycastMesh(obj: Object3D): BufferGeometry<any> | null;
10
+ /**
11
+ * Set the raycast mesh for an object. The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
12
+ * @param obj the object to set the raycast mesh for
13
+ * @param geom the raycast mesh
14
+ */
15
+ export declare function setRaycastMesh(obj: Object3D, geom: BufferGeometry): void;
package/lib/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { BufferGeometry } from "three";
1
2
  export function getParam(name) {
2
3
  const url = new URL(window.location.href);
3
4
  const param = url.searchParams.get(name);
@@ -34,3 +35,26 @@ export function resolveUrl(source, uri) {
34
35
  }
35
36
  return uri;
36
37
  }
38
+ /**
39
+ * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
40
+ * @param obj the object to get the raycast mesh from
41
+ * @returns the raycast mesh or null if not set
42
+ */
43
+ export function getRaycastMesh(obj) {
44
+ if (obj.userData?.["needle:raycast-mesh"] instanceof BufferGeometry) {
45
+ return obj.userData["needle:raycast-mesh"];
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * Set the raycast mesh for an object. The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
51
+ * @param obj the object to set the raycast mesh for
52
+ * @param geom the raycast mesh
53
+ */
54
+ export function setRaycastMesh(obj, geom) {
55
+ if (obj.type === "Mesh" || obj.type === "SkinnedMesh") {
56
+ if (!obj.userData)
57
+ obj.userData = {};
58
+ obj.userData["needle:raycast-mesh"] = geom;
59
+ }
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-progressive",
3
- "version": "1.0.0-alpha.13",
3
+ "version": "1.0.0-alpha.15",
4
4
  "description": "three.js support for loading glTF or GLB files that contain progressive loading data",
5
5
  "homepage": "https://needle.tools",
6
6
  "author": {