@needle-tools/gltf-progressive 1.1.0-alpha.2 → 1.2.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/gltf-progressive.js +406 -382
- package/gltf-progressive.min.js +4 -4
- package/gltf-progressive.umd.cjs +4 -4
- package/lib/extension.d.ts +16 -9
- package/lib/extension.js +53 -37
- package/lib/lods_manager.d.ts +1 -1
- package/lib/lods_manager.js +32 -7
- package/lib/plugins/modelviewer.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var fe=Object.defineProperty,
|
|
2
|
-
`,G),null)),
|
|
3
|
-
`,
|
|
1
|
+
var fe=Object.defineProperty,ge=(t,e,r)=>e in t?fe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(ge(t,typeof e!="symbol"?e+"":e,r),r);import{MeshoptDecoder as me}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as pe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as xe}from"three/examples/jsm/loaders/KTX2Loader.js";import{BufferGeometry as q,Mesh as V,Material as ye,Texture as $,TextureLoader as Le,Matrix4 as se,Frustum as ve,Sphere as De,Box3 as oe,Vector3 as B}from"three";import{GLTFLoader as Me}from"three/examples/jsm/loaders/GLTFLoader.js";let X="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",Y="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(X+"draco_decoder.js",{method:"head"}).catch(t=>{X="./include/draco/",Y="./include/ktx2/"});function Oe(t){X=t}function _e(t){Y=t}let z,J,W;function Q(t){z||(z=new pe,z.setDecoderPath(X),z.setDecoderConfig({type:"js"})),W||(W=new xe,W.setTranscoderPath(Y)),J||(J=me),t?W.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Z(t){t.dracoLoader||t.setDRACOLoader(z),t.ktx2Loader||t.setKTX2Loader(W),t.meshoptDecoder||t.setMeshoptDecoder(J)}function ee(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function we(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const n=t.substring(0,r+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}function ne(t){var e;return((e=t.userData)==null?void 0:e["needle:raycast-mesh"])instanceof q?t.userData["needle:raycast-mesh"]:null}function ie(t,e){(t.type==="Mesh"||t.type==="SkinnedMesh")&&(t.userData||(t.userData={}),t.userData["needle:raycast-mesh"]=e)}const k=new Array,P="NEEDLE_progressive",L=ee("debugprogressive"),te=Symbol("needle-progressive-texture"),U=new Map,re=new Set;if(L){let t=function(){e+=1,console.log("Toggle LOD level",e,U),U.forEach((o,s)=>{for(const i of o.keys){const a=s[i];if(a.isBufferGeometry===!0){const u=_.getMeshLODInformation(a),l=u?Math.min(e,u.lods.length):0;s["DEBUG:LOD"]=e,_.assignMeshLOD(s,l),u&&(r=Math.max(r,u.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,_.assignTextureLOD(s,e);break}}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",o=>{o.key==="p"&&t(),o.key==="w"&&(n=!n,re&&re.forEach(s=>{s.name!="BackgroundCubeMaterial"&&"wireframe"in s&&(s.wireframe=n)}))})}function ae(t,e,r){var n;if(!L)return;U.has(t)||U.set(t,{keys:[],sourceId:r});const o=U.get(t);((n=o?.keys)==null?void 0:n.includes(e))==!1&&o.keys.push(e)}const M=class{constructor(t,e){c(this,"parser"),c(this,"url"),c(this,"_isLoadingMesh"),c(this,"loadMesh",r=>{var n,o;if(this._isLoadingMesh)return null;const s=(o=(n=this.parser.json.meshes[r])==null?void 0:n.extensions)==null?void 0:o[P];return s?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>(this._isLoadingMesh=!1,i&&M.registerMesh(this.url,s.guid,i,s.lods.length,void 0,s),i))):null}),L&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return P}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const r="LODS:minmax",n=t[r];if(n!=null)return n;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const s of t)this.getMaterialMinMaxLODsCount(s,e);return t[r]=e,e}L==="verbose"&&console.log("getMaterialMinMaxLODsCount",t);const o=s=>{const i=this.getAssignedLODInformation(s);if(i){const a=this.lodInfos.get(i.key);if(a&&a.lods){e.min_count=Math.min(e.min_count,a.lods.length),e.max_count=Math.max(e.max_count,a.lods.length);for(let u=0;u<a.lods.length;u++){const l=a.lods[u];l.width&&(e.lods[u]=e.lods[u]||{min_height:1/0,max_height:0},e.lods[u].min_height=Math.min(e.lods[u].min_height,l.height),e.lods[u].max_height=Math.max(e.lods[u].max_height,l.height))}}}};if(t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const s=t;for(const i of Object.keys(s.uniforms)){const a=s.uniforms[i].value;a?.isTexture===!0&&o(a)}}else if(t.isMaterial)for(const s of Object.keys(t)){const i=t[s];i?.isTexture===!0&&o(i)}return t[r]=e,e}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const i=t[s];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let n,o;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(r=n?.userData)!=null&&r.LODS){const s=n.userData.LODS;if(o=this.lodInfos.get(s.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof V||t.isMesh===!0){const n=t.geometry,o=this.getAssignedLODInformation(n);if(!o)return Promise.resolve(null);for(const s of k)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(n,e).then(s=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(s)){const i=o.index||0;s=s[i]}s&&n!=s&&s instanceof q&&(t.geometry=s,L&&ae(t,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else L&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t instanceof ye||t.isMaterial===!0){const r=t,n=[],o=new Array;if(L&&re.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const i of Object.keys(s.uniforms)){const a=s.uniforms[i].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,i);n.push(u),o.push(i)}}}else for(const s of Object.keys(r)){const i=r[s];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,r,s);n.push(a),o.push(s)}}return Promise.all(n).then(s=>{const i=new Array;for(let a=0;a<s.length;a++){const u=s[a],l=o[a];u&&u.isTexture===!0?i.push({material:r,slot:l,texture:u,level:e}):i.push({material:r,slot:l,texture:null,level:e})}return i})}if(t instanceof $||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):M.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return null;if(o?.isTexture===!0){if(o!=t){if(r&&n){const s=r[n];if(s){const i=this.getAssignedLODInformation(s);if(i&&i?.level<e)return L==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,s,o),null}r[n]=o}if(L&&n&&r){const s=this.getAssignedLODInformation(t);s&&ae(r,n,s.url)}}return o}else L=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(o=>(console.error("Error loading LOD",t,o),null))}afterRoot(t){var e,r;return L&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[P];if(s){let i=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===o&&(i=!0,M.registerTexture(this.url,a,s.lods.length,o,s));i||this.parser.getDependency("texture",o).then(a=>{a&&M.registerTexture(this.url,a,s.lods.length,o,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[P];if(s&&s.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const a=this.parser.associations.get(i);a.meshes===o&&M.registerMesh(this.url,s.guid,i,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var r,n,o,s;const i=L=="verbose",a=t.userData.LODS;if(!a)return null;const u=a?.key;let l;if(t.isTexture===!0){const y=t;y.source&&y.source[te]&&(l=y.source[te])}if(l||(l=M.lodInfos.get(u)),l){if(e>0){let f=!1;const w=Array.isArray(l.lods);if(w&&e>=l.lods.length?f=!0:w||(f=!0),f)return this.lowresCache.get(u)}const y=Array.isArray(l.lods)?(r=l.lods[e])==null?void 0:r.path:l.lods;if(!y)return L&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const h=we(a.url,y);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=h+"_"+l.guid,w=this.previouslyLoaded.get(f);if(w!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let m=await w.catch(G=>(console.error(`Error loading LOD ${e} from ${h}
|
|
2
|
+
`,G),null)),b=!1;if(m==null||(m instanceof $&&t instanceof $?(n=m.image)!=null&&n.data||(o=m.source)!=null&&o.data?m=this.copySettings(t,m):(b=!0,this.previouslyLoaded.delete(f)):m instanceof q&&t instanceof q&&((s=m.attributes.position)!=null&&s.array||(b=!0,this.previouslyLoaded.delete(f)))),!b)return m}const g=l,S=new Promise(async(m,b)=>{const G=new Me;Z(G),L&&(await new Promise(x=>setTimeout(x,1e3)),i&&console.warn("Start loading (delayed) "+h,g.guid));let N=h;if(g&&Array.isArray(g.lods)){const x=g.lods[e];x.hash&&(N+="?v="+x.hash)}const p=await G.loadAsync(N).catch(x=>(console.error(`Error loading LOD ${e} from ${h}
|
|
3
|
+
`,x),null));if(!p)return null;const v=p.parser;i&&console.log("Loading finished "+h,g.guid);let D=0;if(p.parser.json.textures){let x=!1;for(const d of p.parser.json.textures){if(d!=null&&d.extensions){const O=d?.extensions[P];if(O!=null&&O.guid&&O.guid===g.guid){x=!0;break}}D++}if(x){let d=await v.getDependency("texture",D);return d&&M.assignLODInformation(a.url,d,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',h,D,d,f),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=g.guid),m(d)}else L&&console.warn("Could not find texture with guid",g.guid)}if(D=0,p.parser.json.meshes){let x=!1;for(const d of p.parser.json.meshes){if(d!=null&&d.extensions){const O=d?.extensions[P];if(O!=null&&O.guid&&O.guid===g.guid){x=!0;break}}D++}if(x){const d=await v.getDependency("mesh",D),O=g;if(i&&console.log(`Loaded Mesh "${d.name}"`,h,D,d,f),d.isMesh===!0){const A=d.geometry;return M.assignLODInformation(a.url,A,u,e,void 0,O.density),m(A)}else{const A=new Array;for(let C=0;C<d.children.length;C++){const j=d.children[C];if(j instanceof V){const R=j.geometry;M.assignLODInformation(a.url,R,u,e,C,O.density),A.push(R)}}return m(A)}}}return m(null)});return this.previouslyLoaded.set(f,S),await S}else if(t instanceof $){i&&console.log("Load texture from uri: "+h);const f=await new Le().loadAsync(h);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,i&&console.log(l,f)):L&&console.warn("failed loading",h),f}}else L&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,n,o,s){if(!e)return;e.userData||(e.userData={});const i=new be(t,r,n,o,s);e.userData.LODS=i,e.userData.LOD=n}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),L&&console.warn(`Copying texture settings
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let w=M;c(w,"registerTexture",(t,e,r,o)=>{v&&console.log("> Progressive: register texture",r,e.name,e.uuid,e,o),e.source&&(e.source[ee]=o);const n=o.guid;M.assignLODInformation(t,e,n,0,0,void 0),M.lodInfos.set(n,o),M.lowresCache.set(n,e)}),c(w,"registerMesh",(t,e,r,o,n,s)=>{var i;v&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const a=r.geometry;a.userData||(a.userData={}),M.assignLODInformation(t,a,e,o,n,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),o>0&&!ne(r)&&ie(r,a);for(const l of C)(i=l.onRegisteredNewMesh)==null||i.call(l,r,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class be{constructor(e,r,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}const re=Z("debugprogressive"),Se=Z("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},S=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new De),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new ve),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new I),c(this,"_tempBoxSize",new I),c(this,"_tempBox2Size",new I),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){C.push(t)}static removePlugin(t){const e=C.indexOf(t);e>=0&&C.splice(e,1)}static get(t){return t[le]?t[le]:new S(t)}get plugins(){return C}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;J(this.renderer),this.renderer.render=function(r,o){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const n=e._frame,s=t++;e.onBeforeRender(r,o,s,n),e._originalRender.call(this,r,o),e.onAfterRender(r,o,s,n)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,o){}onAfterRender(t,e,r,o){var n,s;if(this.pause)return;const i=this.renderer.renderLists.get(t,0),a=i.opaque;let u=!0;if(a.length===1){const l=a[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of a){if(h.material&&(((n=h.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){re&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}const x=i.transparent;for(const h of x){const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}}}updateLODs(t,e,r,o,n){var s,i;let a=r.userData.LOD_state;if(a||(a=new Te,r.userData.LOD_state=a),a.frames++<2)return;for(const l of C)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,a,o,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of C)(i=l.onAfterUpdatedLOD)==null||i.call(l,this.renderer,t,e,r,E);a.lastLodLevel_Mesh=E.mesh_lod,a.lasLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}const r=t;let o=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(o=!0),o&&(r.NEEDLE_LOD=e,w.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return w.assignMeshLOD(t,e).then(o=>(o&&t.userData.LOD==e&&r!=t.geometry,o))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,o=t.max,n=(r.x+o.x)*.5,s=(r.y+o.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,o,n){var s;if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let i=10+1;if(re&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=w.getMeshLODInformation(e.geometry),u=a?.lods,l=u&&u.length>0,x=w.getMaterialMinMaxLODsCount(e.material),h=x?.min!=1/0&&x.min>0&&x.max>0;if(!l&&!h){n.mesh_lod=0,n.texture_lod=0;return}if(l||(i=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){n.mesh_lod=99,n.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const b=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),S.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&b.fov>70){const g=this._tempBox.min,L=this._tempBox.max;let D=g.x,y=g.y,d=L.x,O=L.y;const T=2,B=1.5,j=(g.x+L.x)*.5,R=(g.y+L.y)*.5;D=(D-j)*T+j,y=(y-R)*T+R,d=(d-j)*T+j,O=(O-R)*T+R;const de=D<0&&d>0?0:Math.min(Math.abs(g.x),Math.abs(L.x)),he=y<0&&O>0?0:Math.min(Math.abs(g.y),Math.abs(L.y)),K=Math.max(de,he);r.lastCentrality=(B-K)*(B-K)*(B-K)}else r.lastCentrality=1;const m=this._tempBox.getSize(this._tempBoxSize);m.multiplyScalar(.5),screen.availHeight>0&&m.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),m.x*=b.aspect;const k=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(k);const _=p.getSize(this._tempBox2Size),G=Math.max(_.x,_.y);if(Math.max(m.x,m.y)!=0&&G!=0&&(m.z=_.z/Math.max(_.x,_.y)*Math.max(m.x,m.y)),r.lastScreenCoverage=Math.max(m.x,m.y,m.z),r.lastScreenspaceVolume.copy(m),r.lastScreenCoverage*=r.lastCentrality,re&&S.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const L=S.corner0,D=S.corner1,y=S.corner2,d=S.corner3;L.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=L.x,y.copy(this._tempBox.max),y.y=L.y,d.copy(this._tempBox.max);const O=(L.z+d.z)*.5;L.z=D.z=y.z=d.z=O,L.applyMatrix4(g),D.applyMatrix4(g),y.applyMatrix4(g),d.applyMatrix4(g),S.debugDrawLine(L,D,255),S.debugDrawLine(L,y,255),S.debugDrawLine(D,d,255),S.debugDrawLine(y,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let g=0;g<u.length;g++)if(u[g].density/r.lastScreenCoverage<o){N=g;break}}N<i&&(i=N)}if(n.mesh_lod=i,x!=null&&x.min&&x.min>0&&x.max>0){const b=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=Ee(x.max,0,b)}else n.texture_lod=0}};let P=S;c(P,"debugDrawLine"),c(P,"corner0",new I),c(P,"corner1",new I),c(P,"corner2",new I),c(P,"corner3",new I),c(P,"_tempPtInside",new I);function Ee(t,e,r){return t+(e-t)*r}class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",0),c(this,"lasLodLevel_Texture",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new I),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let o=t;o!=null;o=Object.getPrototypeOf(o)){const n=Object.getOwnPropertySymbols(o),s=n.find(a=>a.toString()=="Symbol(renderer)"),i=n.find(a=>a.toString()=="Symbol(scene)");!e&&s!=null&&(e=t[s].threeRenderer),!r&&i!=null&&(r=t[i])}if(e){console.log("Adding Needle LODs to modelviewer");const o=P.get(e);if(P.addPlugin(new Ae(t)),o.enable(),r){const n=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];n&&e.render(r,n)}return()=>{o.disable()}}return null}class Ae{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,o,n){this.tryParseMeshLOD(r,n),this.tryParseTextureLOD(r,n)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[X]==!0)return;r[X]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&r.material){let s=function(a){var u,l,x;if(a[X]==!0)return;a[X]=!0,a.userData&&(a.userData.LOD=-1);const h=Object.keys(a);for(let f=0;f<h.length;f++){const b=h[f],m=a[b];if(m?.isTexture===!0){const k=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=o.parser.json.textures[k];if(!p){console.warn("Texture data not found for texture index "+k);continue}if((x=p?.extensions)!=null&&x[A]){const _=p.extensions[A];_&&n&&w.registerTexture(n,m,_.lods.length,_)}}}};const i=r.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}tryParseMeshLOD(e,r){var o,n;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const i=(n=(o=r.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[A];if(i&&s){const a=r.uuid;w.registerMesh(s,a,r,0,i.lods.length,i)}}}function Pe(t,e,r,o){J(e),Q(r),r.register(s=>new w(s,t));const n=P.get(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{A as EXTENSION_NAME,P as LODsManager,w as NEEDLE_progressive,Q as addDracoAndKTX2Loaders,J as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,we as setKTX2TranscoderLocation,ie as setRaycastMesh,Pe as useNeedleProgressive};
|
|
5
|
+
`,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let _=M;c(_,"registerTexture",(t,e,r,n,o)=>{L&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,o),e.source&&(e.source[te]=o);const s=o.guid;M.assignLODInformation(t,e,s,r,n,void 0),M.lodInfos.set(s,o),M.lowresCache.set(s,e)}),c(_,"registerMesh",(t,e,r,n,o,s)=>{var i;L&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const a=r.geometry;a.userData||(a.userData={}),M.assignLODInformation(t,a,e,n,o,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),n>0&&!ne(r)&&ie(r,a);for(const l of k)(i=l.onRegisteredNewMesh)==null||i.call(l,r,s)}),c(_,"lodInfos",new Map),c(_,"previouslyLoaded",new Map),c(_,"lowresCache",new Map);class be{constructor(e,r,n,o,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=n,o!=null&&(this.index=o),s!=null&&(this.density=s)}}const F=ee("debugprogressive"),Se=ee("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},T=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new ve),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new De),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new B),c(this,"_tempBoxSize",new B),c(this,"_tempBox2Size",new B),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){k.push(t)}static removePlugin(t){const e=k.indexOf(t);e>=0&&k.splice(e,1)}static get(t){return t[le]?t[le]:new T(t)}get plugins(){return k}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;Q(this.renderer),this.renderer.render=function(r,n){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const o=e._frame,s=t++;e.onBeforeRender(r,n,s,o),e._originalRender.call(this,r,n),e.onAfterRender(r,n,s,o)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,n){}onAfterRender(t,e,r,n){var o,s;if(this.pause)return;const i=this.renderer.renderLists.get(t,0),a=i.opaque;let u=!0;if(a.length===1){const l=a[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&n%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of a){if(h.material&&(((o=h.geometry)==null?void 0:o.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){F&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}const y=i.transparent;for(const h of y){const f=h.object;(f instanceof V||f.isMesh)&&this.updateLODs(t,e,f,l,n)}}}updateLODs(t,e,r,n,o){var s,i;let a=r.userData.LOD_state;if(a||(a=new Te,r.userData.LOD_state=a),a.frames++<2)return;for(const l of k)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,a,n,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of k)(i=l.onAfterUpdatedLOD)==null||i.call(l,this.renderer,t,e,r,E);a.lastLodLevel_Mesh=E.mesh_lod,a.lastLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}const r=t;let n=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(n=!0),n&&(r.NEEDLE_LOD=e,_.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return _.assignMeshLOD(t,e).then(n=>(n&&t.userData.LOD==e&&r!=t.geometry,n))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,n=t.max,o=(r.x+n.x)*.5,s=(r.y+n.y)*.5;return this._tempPtInside.set(o,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,n,o){var s;if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=10+1;if(F&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=_.getMeshLODInformation(e.geometry),u=a?.lods,l=u&&u.length>0,y=_.getMaterialMinMaxLODsCount(e.material),h=y?.min_count!=1/0&&y.min_count>0&&y.max_count>0;if(!l&&!h){o.mesh_lod=0,o.texture_lod=0;return}if(l||(i=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){o.mesh_lod=99,o.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const w=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const p=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(p)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),T.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&w.fov>70){const p=this._tempBox.min,v=this._tempBox.max;let D=p.x,x=p.y,d=v.x,O=v.y;const A=2,C=1.5,j=(p.x+v.x)*.5,R=(p.y+v.y)*.5;D=(D-j)*A+j,x=(x-R)*A+R,d=(d-j)*A+j,O=(O-R)*A+R;const de=D<0&&d>0?0:Math.min(Math.abs(p.x),Math.abs(v.x)),he=x<0&&O>0?0:Math.min(Math.abs(p.y),Math.abs(v.y)),H=Math.max(de,he);r.lastCentrality=(C-H)*(C-H)*(C-H)}else r.lastCentrality=1;const g=this._tempBox.getSize(this._tempBoxSize);g.multiplyScalar(.5),screen.availHeight>0&&g.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),g.x*=w.aspect;const S=t.matrixWorldInverse,m=this._tempBox2;m.copy(f),m.applyMatrix4(e.matrixWorld),m.applyMatrix4(S);const b=m.getSize(this._tempBox2Size),G=Math.max(b.x,b.y);if(Math.max(g.x,g.y)!=0&&G!=0&&(g.z=b.z/Math.max(b.x,b.y)*Math.max(g.x,g.y)),r.lastScreenCoverage=Math.max(g.x,g.y,g.z),r.lastScreenspaceVolume.copy(g),r.lastScreenCoverage*=r.lastCentrality,F&&T.debugDrawLine){const p=this.tempMatrix.copy(this.projectionScreenMatrix);p.invert();const v=T.corner0,D=T.corner1,x=T.corner2,d=T.corner3;v.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=v.x,x.copy(this._tempBox.max),x.y=v.y,d.copy(this._tempBox.max);const O=(v.z+d.z)*.5;v.z=D.z=x.z=d.z=O,v.applyMatrix4(p),D.applyMatrix4(p),x.applyMatrix4(p),d.applyMatrix4(p),T.debugDrawLine(v,D,255),T.debugDrawLine(v,x,255),T.debugDrawLine(D,d,255),T.debugDrawLine(x,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let p=0;p<u.length;p++)if(u[p].density/r.lastScreenCoverage<n){N=p;break}}N<i&&(i=N)}if(o.mesh_lod=i,h)if(r.lastLodLevel_Texture<0){if(o.texture_lod=y.max_count-1,F){const w=y.lods[y.max_count-1];F&&console.log(`First Texture LOD ${o.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenCoverage*1.5,g=this.renderer.domElement.clientHeight/window.devicePixelRatio*w;for(let S=y.lods.length-1;S>=0;S--){const m=y.lods[S];if(m.max_height>g){o.texture_lod=S,o.texture_lod<r.lastLodLevel_Texture&&F&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} (${m.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${g.toFixed(0)}px) - ${e.name}`);break}}}else o.texture_lod=0}};let I=T;c(I,"debugDrawLine"),c(I,"corner0",new B),c(I,"corner1",new B),c(I,"corner2",new B),c(I,"corner3",new B),c(I,"_tempPtInside",new B);class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",-1),c(this,"lastLodLevel_Texture",-1),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new B),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),K=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let n=t;n!=null;n=Object.getPrototypeOf(n)){const o=Object.getOwnPropertySymbols(n),s=o.find(a=>a.toString()=="Symbol(renderer)"),i=o.find(a=>a.toString()=="Symbol(scene)");!e&&s!=null&&(e=t[s].threeRenderer),!r&&i!=null&&(r=t[i])}if(e){console.log("Adding Needle LODs to modelviewer");const n=I.get(e);if(I.addPlugin(new Ee(t)),n.enable(),r){const o=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];o&&e.render(r,o)}return()=>{n.disable()}}return null}class Ee{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,n,o){this.tryParseMeshLOD(r,o),this.tryParseTextureLOD(r,o)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[K]==!0)return;r[K]=!0;const n=this.tryGetCurrentGLTF(e),o=this.getUrl();if(o&&n&&r.material){let s=function(a){var u,l,y;if(a[K]==!0)return;a[K]=!0,a.userData&&(a.userData.LOD=-1);const h=Object.keys(a);for(let f=0;f<h.length;f++){const w=h[f],g=a[w];if(g?.isTexture===!0){const S=(l=(u=g.userData)==null?void 0:u.associations)==null?void 0:l.textures,m=n.parser.json.textures[S];if(!m){console.warn("Texture data not found for texture index "+S);continue}if((y=m?.extensions)!=null&&y[P]){const b=m.extensions[P];b&&o&&_.registerTexture(o,g,b.lods.length,S,b)}}}};const i=r.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}tryParseMeshLOD(e,r){var n,o;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const i=(o=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[P];if(i&&s){const a=r.uuid;_.registerMesh(s,a,r,0,i.lods.length,i)}}}function Ae(t,e,r,n){Q(e),Z(r),r.register(s=>new _(s,t));const o=I.get(e);return n?.enableLODsManager!==!1&&o.enable(),o}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{P as EXTENSION_NAME,I as LODsManager,_ as NEEDLE_progressive,Z as addDracoAndKTX2Loaders,Q as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,_e as setKTX2TranscoderLocation,ie as setRaycastMesh,Ae as useNeedleProgressive};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,F),null)),
|
|
3
|
-
`,p),null));if(!B)return null;const
|
|
1
|
+
"use strict";var fe=Object.defineProperty;var he=(l,e,t)=>e in l?fe(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var c=(l,e,t)=>(he(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ge=require("three/examples/jsm/libs/meshopt_decoder.module.js"),pe=require("three/examples/jsm/loaders/DRACOLoader.js"),ye=require("three/examples/jsm/loaders/KTX2Loader.js"),g=require("three"),Le=require("three/examples/jsm/loaders/GLTFLoader.js");let X="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ee="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(X+"draco_decoder.js",{method:"head"}).catch(l=>{X="./include/draco/",ee="./include/ktx2/"});function xe(l){X=l}function me(l){ee=l}let $,Z,U;function te(l){$||($=new pe.DRACOLoader,$.setDecoderPath(X),$.setDecoderConfig({type:"js"})),U||(U=new ye.KTX2Loader,U.setTranscoderPath(ee)),Z||(Z=ge.MeshoptDecoder),l?U.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function re(l){l.dracoLoader||l.setDRACOLoader($),l.ktx2Loader||l.setKTX2Loader(U),l.meshoptDecoder||l.setMeshoptDecoder(Z)}function se(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function De(l,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||l===void 0)return e;const t=l.lastIndexOf("/");if(t>=0){const r=l.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}function ae(l){var e;return((e=l.userData)==null?void 0:e["needle:raycast-mesh"])instanceof g.BufferGeometry?l.userData["needle:raycast-mesh"]:null}function le(l,e){(l.type==="Mesh"||l.type==="SkinnedMesh")&&(l.userData||(l.userData={}),l.userData["needle:raycast-mesh"]=e)}const I=new Array,R="NEEDLE_progressive",w=se("debugprogressive"),Q=Symbol("needle-progressive-texture"),W=new Map,j=new Set;if(w){let l=function(){e+=1,console.log("Toggle LOD level",e,W),W.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n.isBufferGeometry===!0){const a=S.getMeshLODInformation(n),d=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,d),a&&(t=Math.max(t,a.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,S.assignTextureLOD(o,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&l(),i.key==="w"&&(r=!r,j&&j.forEach(o=>{o.name!="BackgroundCubeMaterial"&&"wireframe"in o&&(o.wireframe=r)}))})}function ie(l,e,t){var i;if(!w)return;W.has(l)||W.set(l,{keys:[],sourceId:t});const r=W.get(l);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const v=class{constructor(e,t){c(this,"parser");c(this,"url");c(this,"_isLoadingMesh");c(this,"loadMesh",e=>{var r,i;if(this._isLoadingMesh)return null;const t=(i=(r=this.parser.json.meshes[e])==null?void 0:r.extensions)==null?void 0:i[R];return t?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",e).then(o=>(this._isLoadingMesh=!1,o&&v.registerMesh(this.url,t.guid,o,t.lods.length,void 0,t),o))):null});w&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return R}static getMeshLODInformation(e){const t=this.getAssignedLODInformation(e);return t!=null&&t.key?this.lodInfos.get(t.key):null}static getMaterialMinMaxLODsCount(e,t){const r="LODS:minmax",i=e[r];if(i!=null)return i;if(t||(t={min_count:1/0,max_count:0,lods:[]}),Array.isArray(e)){for(const s of e)this.getMaterialMinMaxLODsCount(s,t);return e[r]=t,t}w==="verbose"&&console.log("getMaterialMinMaxLODsCount",e);const o=s=>{const n=this.getAssignedLODInformation(s);if(n){const a=this.lodInfos.get(n.key);if(a&&a.lods){t.min_count=Math.min(t.min_count,a.lods.length),t.max_count=Math.max(t.max_count,a.lods.length);for(let d=0;d<a.lods.length;d++){const h=a.lods[d];h.width&&(t.lods[d]=t.lods[d]||{min_height:1/0,max_height:0},t.lods[d].min_height=Math.min(t.lods[d].min_height,h.height),t.lods[d].max_height=Math.max(t.lods[d].max_height,h.height))}}}};if(e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const s=e;for(const n of Object.keys(s.uniforms)){const a=s.uniforms[n].value;(a==null?void 0:a.isTexture)===!0&&o(a)}}else if(e.isMaterial)for(const s of Object.keys(e)){const n=e[s];(n==null?void 0:n.isTexture)===!0&&o(n)}return e[r]=t,t}static hasLODLevelAvailable(e,t){var o;if(Array.isArray(e)){for(const s of e)if(this.hasLODLevelAvailable(s,t))return!0;return!1}if(e.isMaterial===!0){for(const s of Object.keys(e)){const n=e[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,t))return!0}return!1}else if(e.isGroup===!0){for(const s of e.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,t))return!0}let r,i;if(e.isMesh?r=e.geometry:(e.isBufferGeometry||e.isTexture)&&(r=e),r&&(o=r==null?void 0:r.userData)!=null&&o.LODS){const s=r.userData.LODS;if(i=this.lodInfos.get(s.key),t===void 0)return i!=null;if(i)return Array.isArray(i.lods)?t<i.lods.length:t===0}return!1}static assignMeshLOD(e,t){var r;if(!e)return Promise.resolve(null);if(e instanceof g.Mesh||e.isMesh===!0){const i=e.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of I)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,v.getOrLoadLOD(i,t).then(s=>{if(e["LOD:requested level"]===t){if(delete e["LOD:requested level"],Array.isArray(s)){const n=o.index||0;s=s[n]}s&&i!=s&&s instanceof g.BufferGeometry&&(e.geometry=s,w&&ie(e,"geometry",o.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else w&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",e);return Promise.resolve(null)}static assignTextureLOD(e,t=0){if(!e)return Promise.resolve(null);if(e instanceof g.Material||e.isMaterial===!0){const r=e,i=[],o=new Array;if(w&&j.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const n of Object.keys(s.uniforms)){const a=s.uniforms[n].value;if((a==null?void 0:a.isTexture)===!0){const d=this.assignTextureLODForSlot(a,t,r,n);i.push(d),o.push(n)}}}else for(const s of Object.keys(r)){const n=r[s];if((n==null?void 0:n.isTexture)===!0){const a=this.assignTextureLODForSlot(n,t,r,s);i.push(a),o.push(s)}}return Promise.all(i).then(s=>{const n=new Array;for(let a=0;a<s.length;a++){const d=s[a],h=o[a];d&&d.isTexture===!0?n.push({material:r,slot:h,texture:d,level:t}):n.push({material:r,slot:h,texture:null,level:t})}return n})}if(e instanceof g.Texture||e.isTexture===!0){const r=e;return this.assignTextureLODForSlot(r,t,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(e,t,r,i){return(e==null?void 0:e.isTexture)!==!0?Promise.resolve(null):i==="glyphMap"?Promise.resolve(e):v.getOrLoadLOD(e,t).then(o=>{if(Array.isArray(o))return null;if((o==null?void 0:o.isTexture)===!0){if(o!=e){if(r&&i){const s=r[i];if(s){const n=this.getAssignedLODInformation(s);if(n&&(n==null?void 0:n.level)<t)return w==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,t,r,s,o),null}r[i]=o}if(w&&i&&r){const s=this.getAssignedLODInformation(e);s&&ie(r,i,s.url)}}return o}else w=="verbose"&&console.warn("No LOD found for",e,t);return null}).catch(o=>(console.error("Error loading LOD",e,o),null))}afterRoot(e){var t,r;return w&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[R];if(s){let n=!1;for(const a of this.parser.associations.keys())a.isTexture===!0&&this.parser.associations.get(a).textures===o&&(n=!0,v.registerTexture(this.url,a,s.lods.length,o,s));n||this.parser.getDependency("texture",o).then(a=>{a&&v.registerTexture(this.url,a,s.lods.length,o,s)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[R];if(s&&s.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const a=this.parser.associations.get(n);a.meshes===o&&v.registerMesh(this.url,s.guid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,d,h;const r=w=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const D=e;D.source&&D.source[Q]&&(s=D.source[Q])}if(s||(s=v.lodInfos.get(o)),s){if(t>0){let L=!1;const M=Array.isArray(s.lods);if(M&&t>=s.lods.length?L=!0:M||(L=!0),L)return this.lowresCache.get(o)}const D=Array.isArray(s.lods)?(n=s.lods[t])==null?void 0:n.path:s.lods;if(!D)return w&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const f=De(i.url,D);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const L=f+"_"+s.guid,M=this.previouslyLoaded.get(L);if(M!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${L}`);let x=await M.catch(F=>(console.error(`Error loading LOD ${t} from ${f}
|
|
2
|
+
`,F),null)),A=!1;if(x==null||(x instanceof g.Texture&&e instanceof g.Texture?(a=x.image)!=null&&a.data||(d=x.source)!=null&&d.data?x=this.copySettings(e,x):(A=!0,this.previouslyLoaded.delete(L)):x instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((h=x.attributes.position)!=null&&h.array||(A=!0,this.previouslyLoaded.delete(L)))),!A)return x}const O=s,_=new Promise(async(x,A)=>{const F=new Le.GLTFLoader;re(F),w&&(await new Promise(p=>setTimeout(p,1e3)),r&&console.warn("Start loading (delayed) "+f,O.guid));let Y=f;if(O&&Array.isArray(O.lods)){const p=O.lods[t];p.hash&&(Y+="?v="+p.hash)}const B=await F.loadAsync(Y).catch(p=>(console.error(`Error loading LOD ${t} from ${f}
|
|
3
|
+
`,p),null));if(!B)return null;const H=B.parser;r&&console.log("Loading finished "+f,O.guid);let y=0;if(B.parser.json.textures){let p=!1;for(const u of B.parser.json.textures){if(u!=null&&u.extensions){const m=u==null?void 0:u.extensions[R];if(m!=null&&m.guid&&m.guid===O.guid){p=!0;break}}y++}if(p){let u=await H.getDependency("texture",y);return u&&v.assignLODInformation(i.url,u,o,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+u.name+'"',f,y,u,L),e instanceof g.Texture&&(u=this.copySettings(e,u)),u&&(u.guid=O.guid),x(u)}else w&&console.warn("Could not find texture with guid",O.guid)}if(y=0,B.parser.json.meshes){let p=!1;for(const u of B.parser.json.meshes){if(u!=null&&u.extensions){const m=u==null?void 0:u.extensions[R];if(m!=null&&m.guid&&m.guid===O.guid){p=!0;break}}y++}if(p){const u=await H.getDependency("mesh",y),m=O;if(r&&console.log(`Loaded Mesh "${u.name}"`,f,y,u,L),u.isMesh===!0){const T=u.geometry;return v.assignLODInformation(i.url,T,o,t,void 0,m.density),x(T)}else{const T=new Array;for(let C=0;C<u.children.length;C++){const G=u.children[C];if(G instanceof g.Mesh){const N=G.geometry;v.assignLODInformation(i.url,N,o,t,C,m.density),T.push(N)}}return x(T)}}}return x(null)});return this.previouslyLoaded.set(L,_),await _}else if(e instanceof g.Texture){r&&console.log("Load texture from uri: "+f);const M=await new g.TextureLoader().loadAsync(f);return M?(M.guid=s.guid,M.flipY=!1,M.needsUpdate=!0,M.colorSpace=e.colorSpace,r&&console.log(s,M)):w&&console.warn("failed loading",f),M}}else w&&console.warn(`Can not load LOD ${t}: no LOD info found for "${o}" ${e.name}`,e.type);return null}static assignLODInformation(e,t,r,i,o,s){if(!t)return;t.userData||(t.userData={});const n=new Me(e,r,i,o,s);t.userData.LODS=n,t.userData.LOD=i}static getAssignedLODInformation(e){var t;return((t=e==null?void 0:e.userData)==null?void 0:t.LODS)||null}static copySettings(e,t){return t=t.clone(),w&&console.warn(`Copying texture settings
|
|
4
4
|
`,e.uuid,`
|
|
5
|
-
`,t.uuid),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.mipmaps||(t.generateMipmaps=e.generateMipmaps),t}};let S=_;c(S,"registerTexture",(e,t,r,s)=>{v&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,s),t.source&&(t.source[J]=s);const n=s.guid;_.assignLODInformation(e,t,n,0,0,void 0),_.lodInfos.set(n,s),_.lowresCache.set(n,t)}),c(S,"registerMesh",(e,t,r,s,n,i)=>{var d;v&&console.log("> Progressive: register mesh",n,r.name,i,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),_.assignLODInformation(e,o,t,s,n,i.density),_.lodInfos.set(t,i);let l=_.lowresCache.get(t);l?l.push(r.geometry):l=[r.geometry],_.lowresCache.set(t,l),s>0&&!ae(r)&&le(r,o);for(const h of R)(d=h.onRegisteredNewMesh)==null||d.call(h,r,i)}),c(S,"lodInfos",new Map),c(S,"previouslyLoaded",new Map),c(S,"lowresCache",new Map);class Me{constructor(e,t,r,s,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,s!=null&&(this.index=s),n!=null&&(this.density=n)}}const Q=se("debugprogressive"),Oe=se("noprogressive"),ne=Symbol("Needle:LODSManager"),P={mesh_lod:-1,texture_lod:-1},A=class{constructor(e){c(this,"renderer");c(this,"projectionScreenMatrix",new g.Matrix4);c(this,"cameraFrustrum",new g.Frustum);c(this,"targetTriangleDensity",2e5);c(this,"updateInterval",0);c(this,"pause",!1);c(this,"_frame",0);c(this,"_originalRender");c(this,"_sphere",new g.Sphere);c(this,"_tempBox",new g.Box3);c(this,"_tempBox2",new g.Box3);c(this,"tempMatrix",new g.Matrix4);c(this,"_tempWorldPosition",new g.Vector3);c(this,"_tempBoxSize",new g.Vector3);c(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){R.push(e)}static removePlugin(e){const t=R.indexOf(e);t>=0&&R.splice(t,1)}static get(e){return e[ne]?e[ne]:new A(e)}get plugins(){return R}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,s){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const i=t._frame,o=e++;t.onBeforeRender(r,s,o,i),t._originalRender.call(this,r,s),t.onAfterRender(r,s,o,i)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,s){}onAfterRender(e,t,r,s){var l,d;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),i=n.opaque;let o=!0;if(i.length===1){const h=i[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(o=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(o=!1),o){if(Oe||this.updateInterval>0&&s%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of i){if(f.material&&(((l=f.geometry)==null?void 0:l.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){Q&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,s)}const L=n.transparent;for(const f of L){const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,s)}}}updateLODs(e,t,r,s,n){var l,d;let i=r.userData.LOD_state;if(i||(i=new _e,r.userData.LOD_state=i),i.frames++<2)return;for(const h of R)(l=h.onBeforeUpdateLOD)==null||l.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,i,s,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod);let o=P.texture_lod;if(r.material&&o>=0){const h=r["DEBUG:LOD"];h!=null&&(o=h),this.loadProgressiveTextures(r.material,o)}for(const h of R)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,P);i.lastLodLevel_Mesh=P.mesh_lod,i.lasLodLevel_Texture=P.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const n of e)this.loadProgressiveTextures(n,t);return}const r=e;let s=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(s=!0),s&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return S.assignMeshLOD(e,t).then(s=>(s&&e.userData.LOD==t&&r!=e.geometry,s))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,s=e.max,n=(r.x+s.x)*.5,i=(r.y+s.y)*.5;return this._tempPtInside.set(n,i,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,s,n){var D;if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}let o=10+1;if(Q&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const l=S.getMeshLODInformation(t.geometry),d=l==null?void 0:l.lods,h=d&&d.length>0,L=S.getMaterialMinMaxLODsCount(t.material),f=(L==null?void 0:L.min)!=1/0&&L.min>0&&L.max>0;if(!h&&!f){n.mesh_lod=0,n.texture_lod=0;return}if(h||(o=0),!((D=this.cameraFrustrum)!=null&&D.intersectsObject(t))){n.mesh_lod=99,n.texture_lod=99;return}const m=t.geometry.boundingBox;if(m&&e.isPerspectiveCamera){const O=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(m),this._tempBox.applyMatrix4(t.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let u=y.x,x=y.y,T=p.x,C=p.y;const I=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;u=(u-V)*I+V,x=(x-q)*I+q,T=(T-V)*I+V,C=(C-q)*I+q;const ue=u<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),fe=x<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),H=Math.max(ue,fe);r.lastCentrality=(N-H)*(N-H)*(N-H)}else r.lastCentrality=1;const M=this._tempBox.getSize(this._tempBoxSize);M.multiplyScalar(.5),screen.availHeight>0&&M.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),M.x*=O.aspect;const G=e.matrixWorldInverse,w=this._tempBox2;w.copy(m),w.applyMatrix4(t.matrixWorld),w.applyMatrix4(G);const E=w.getSize(this._tempBox2Size),F=Math.max(E.x,E.y);if(Math.max(M.x,M.y)!=0&&F!=0&&(M.z=E.z/Math.max(E.x,E.y)*Math.max(M.x,M.y)),r.lastScreenCoverage=Math.max(M.x,M.y,M.z),r.lastScreenspaceVolume.copy(M),r.lastScreenCoverage*=r.lastCentrality,Q&&A.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=A.corner0,u=A.corner1,x=A.corner2,T=A.corner3;p.copy(this._tempBox.min),u.copy(this._tempBox.max),u.x=p.x,x.copy(this._tempBox.max),x.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=u.z=x.z=T.z=C,p.applyMatrix4(y),u.applyMatrix4(y),x.applyMatrix4(y),T.applyMatrix4(y),A.debugDrawLine(p,u,255),A.debugDrawLine(p,x,255),A.debugDrawLine(u,T,255),A.debugDrawLine(x,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<s){B=y;break}}B<o&&(o=B)}if(n.mesh_lod=o,L!=null&&L.min&&L.min>0&&L.max>0){const O=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=we(L.max,0,O)}else n.texture_lod=0}};let b=A;c(b,"debugDrawLine"),c(b,"corner0",new g.Vector3),c(b,"corner1",new g.Vector3),c(b,"corner2",new g.Vector3),c(b,"corner3",new g.Vector3),c(b,"_tempPtInside",new g.Vector3);function we(a,e,t){return a+(e-a)*t}class _e{constructor(){c(this,"frames",0);c(this,"lastLodLevel_Mesh",0);c(this,"lasLodLevel_Texture",0);c(this,"lastScreenCoverage",0);c(this,"lastScreenspaceVolume",new g.Vector3);c(this,"lastCentrality",0)}}const oe=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(a){if(!a)return null;let e=null,t=null;for(let r=a;r!=null;r=Object.getPrototypeOf(r)){const s=Object.getOwnPropertySymbols(r),n=s.find(o=>o.toString()=="Symbol(renderer)"),i=s.find(o=>o.toString()=="Symbol(scene)");!e&&n!=null&&(e=a[n].threeRenderer),!t&&i!=null&&(t=a[i])}if(e){console.log("Adding Needle LODs to modelviewer");const r=b.get(e);if(b.addPlugin(new ve(a)),r.enable(),t){const s=t.camera||t.traverse(n=>n.type=="PerspectiveCamera")[0];s&&e.render(t,s)}return()=>{r.disable()}}return null}class ve{constructor(e){c(this,"modelviewer");c(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,s){this.tryParseMeshLOD(t,s),this.tryParseTextureLOD(t,s)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[X]==!0)return;t[X]=!0;const r=this.tryGetCurrentGLTF(e),s=this.getUrl();if(s&&r&&t.material){let n=function(o){var d,h,L;if(o[X]==!0)return;o[X]=!0,o.userData&&(o.userData.LOD=-1);const l=Object.keys(o);for(let f=0;f<l.length;f++){const m=l[f],D=o[m];if((D==null?void 0:D.isTexture)===!0){const O=(h=(d=D.userData)==null?void 0:d.associations)==null?void 0:h.textures,M=r.parser.json.textures[O];if(!M){console.warn("Texture data not found for texture index "+O);continue}if((L=M==null?void 0:M.extensions)!=null&&L[k]){const G=M.extensions[k];G&&s&&S.registerTexture(s,D,G.lods.length,G)}}}};const i=t.material;if(Array.isArray(i))for(const o of i)n(o);else n(i)}}tryParseMeshLOD(e,t){var n,i;if(t[oe]==!0)return;t[oe]=!0;const r=this.getUrl();if(!r)return;const s=(i=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:i[k];if(s&&r){const o=t.uuid;S.registerMesh(r,o,t,0,s.lods.length,s)}}}function Se(a,e,t,r){te(e),re(t),t.register(n=>new S(n,a));const s=b.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&s.enable(),s}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=k;exports.LODsManager=b;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=Le;exports.setKTX2TranscoderLocation=xe;exports.setRaycastMesh=le;exports.useNeedleProgressive=Se;
|
|
5
|
+
`,t.uuid),t.offset=e.offset,t.repeat=e.repeat,t.colorSpace=e.colorSpace,t.magFilter=e.magFilter,t.minFilter=e.minFilter,t.wrapS=e.wrapS,t.wrapT=e.wrapT,t.flipY=e.flipY,t.anisotropy=e.anisotropy,t.mipmaps||(t.generateMipmaps=e.generateMipmaps),t}};let S=v;c(S,"registerTexture",(e,t,r,i,o)=>{w&&console.log("> Progressive: register texture",i,t.name,t.uuid,t,o),t.source&&(t.source[Q]=o);const s=o.guid;v.assignLODInformation(e,t,s,r,i,void 0),v.lodInfos.set(s,o),v.lowresCache.set(s,t)}),c(S,"registerMesh",(e,t,r,i,o,s)=>{var d;w&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;n.userData||(n.userData={}),v.assignLODInformation(e,n,t,i,o,s.density),v.lodInfos.set(t,s);let a=v.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],v.lowresCache.set(t,a),i>0&&!ae(r)&&le(r,n);for(const h of I)(d=h.onRegisteredNewMesh)==null||d.call(h,r,s)}),c(S,"lodInfos",new Map),c(S,"previouslyLoaded",new Map),c(S,"lowresCache",new Map);class Me{constructor(e,t,r,i,o){c(this,"url");c(this,"key");c(this,"level");c(this,"index");c(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const z=se("debugprogressive"),Oe=se("noprogressive"),oe=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},b=class{constructor(e){c(this,"renderer");c(this,"projectionScreenMatrix",new g.Matrix4);c(this,"cameraFrustrum",new g.Frustum);c(this,"targetTriangleDensity",2e5);c(this,"updateInterval",0);c(this,"pause",!1);c(this,"_frame",0);c(this,"_originalRender");c(this,"_sphere",new g.Sphere);c(this,"_tempBox",new g.Box3);c(this,"_tempBox2",new g.Box3);c(this,"tempMatrix",new g.Matrix4);c(this,"_tempWorldPosition",new g.Vector3);c(this,"_tempBoxSize",new g.Vector3);c(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){I.push(e)}static removePlugin(e){const t=I.indexOf(e);t>=0&&I.splice(t,1)}static get(e){return e[oe]?e[oe]:new b(e)}get plugins(){return I}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,i){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const s=t._frame,n=e++;t.onBeforeRender(r,i,n,s),t._originalRender.call(this,r,i),t.onAfterRender(r,i,n,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var a,d;if(this.pause)return;const o=this.renderer.renderLists.get(e,0),s=o.opaque;let n=!0;if(s.length===1){const h=s[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(n=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(n=!1),n){if(Oe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of s){if(f.material&&(((a=f.geometry)==null?void 0:a.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){z&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const L=f.object;(L instanceof g.Mesh||L.isMesh)&&this.updateLODs(e,t,L,h,i)}const D=o.transparent;for(const f of D){const L=f.object;(L instanceof g.Mesh||L.isMesh)&&this.updateLODs(e,t,L,h,i)}}}updateLODs(e,t,r,i,o){var a,d;let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const h of I)(a=h.onBeforeUpdateLOD)==null||a.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,s,i,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let n=E.texture_lod;if(r.material&&n>=0){const h=r["DEBUG:LOD"];h!=null&&(n=h),this.loadProgressiveTextures(r.material,n)}for(const h of I)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,E);s.lastLodLevel_Mesh=E.mesh_lod,s.lastLodLevel_Texture=E.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const o of e)this.loadProgressiveTextures(o,t);return}const r=e;let i=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(i=!0),i&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return S.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,i=e.max,o=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(o,s,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,i,o){var M;if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}let n=10+1;if(z&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const a=S.getMeshLODInformation(t.geometry),d=a==null?void 0:a.lods,h=d&&d.length>0,D=S.getMaterialMinMaxLODsCount(t.material),f=(D==null?void 0:D.min_count)!=1/0&&D.min_count>0&&D.max_count>0;if(!h&&!f){o.mesh_lod=0,o.texture_lod=0;return}if(h||(n=0),!((M=this.cameraFrustrum)!=null&&M.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const L=t.geometry.boundingBox;if(L&&e.isPerspectiveCamera){const O=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(L),this._tempBox.applyMatrix4(t.matrixWorld),b.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let u=y.x,m=y.y,T=p.x,C=p.y;const G=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;u=(u-V)*G+V,m=(m-q)*G+q,T=(T-V)*G+V,C=(C-q)*G+q;const ue=u<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),de=m<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),J=Math.max(ue,de);r.lastCentrality=(N-J)*(N-J)*(N-J)}else r.lastCentrality=1;const _=this._tempBox.getSize(this._tempBoxSize);_.multiplyScalar(.5),screen.availHeight>0&&_.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),_.x*=O.aspect;const k=e.matrixWorldInverse,x=this._tempBox2;x.copy(L),x.applyMatrix4(t.matrixWorld),x.applyMatrix4(k);const A=x.getSize(this._tempBox2Size),F=Math.max(A.x,A.y);if(Math.max(_.x,_.y)!=0&&F!=0&&(_.z=A.z/Math.max(A.x,A.y)*Math.max(_.x,_.y)),r.lastScreenCoverage=Math.max(_.x,_.y,_.z),r.lastScreenspaceVolume.copy(_),r.lastScreenCoverage*=r.lastCentrality,z&&b.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=b.corner0,u=b.corner1,m=b.corner2,T=b.corner3;p.copy(this._tempBox.min),u.copy(this._tempBox.max),u.x=p.x,m.copy(this._tempBox.max),m.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=u.z=m.z=T.z=C,p.applyMatrix4(y),u.applyMatrix4(y),m.applyMatrix4(y),T.applyMatrix4(y),b.debugDrawLine(p,u,255),b.debugDrawLine(p,m,255),b.debugDrawLine(u,T,255),b.debugDrawLine(m,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<i){B=y;break}}B<n&&(n=B)}if(o.mesh_lod=n,f)if(r.lastLodLevel_Texture<0){if(o.texture_lod=D.max_count-1,z){const O=D.lods[D.max_count-1];z&&console.log(`First Texture LOD ${o.texture_lod} (${O.max_height}px) - ${t.name}`)}}else{const O=r.lastScreenCoverage*1.5,k=this.renderer.domElement.clientHeight/window.devicePixelRatio*O;for(let x=D.lods.length-1;x>=0;x--){const A=D.lods[x];if(A.max_height>k){o.texture_lod=x,o.texture_lod<r.lastLodLevel_Texture&&z&&console.log(`Texture LOD changed ${r.lastLodLevel_Texture} → ${o.texture_lod} (${A.max_height}px: ${(100*r.lastScreenCoverage).toFixed(2)} % = ${k.toFixed(0)}px) - ${t.name}`);break}}}else o.texture_lod=0}};let P=b;c(P,"debugDrawLine"),c(P,"corner0",new g.Vector3),c(P,"corner1",new g.Vector3),c(P,"corner2",new g.Vector3),c(P,"corner3",new g.Vector3),c(P,"_tempPtInside",new g.Vector3);class _e{constructor(){c(this,"frames",0);c(this,"lastLodLevel_Mesh",-1);c(this,"lastLodLevel_Texture",-1);c(this,"lastScreenCoverage",0);c(this,"lastScreenspaceVolume",new g.Vector3);c(this,"lastCentrality",0)}}const ne=Symbol("NEEDLE_mesh_lod"),K=Symbol("NEEDLE_texture_lod");function ce(l){if(!l)return null;let e=null,t=null;for(let r=l;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),o=i.find(n=>n.toString()=="Symbol(renderer)"),s=i.find(n=>n.toString()=="Symbol(scene)");!e&&o!=null&&(e=l[o].threeRenderer),!t&&s!=null&&(t=l[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=P.get(e);if(P.addPlugin(new we(l)),r.enable(),t){const i=t.camera||t.traverse(o=>o.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class we{constructor(e){c(this,"modelviewer");c(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[K]==!0)return;t[K]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let o=function(n){var d,h,D;if(n[K]==!0)return;n[K]=!0,n.userData&&(n.userData.LOD=-1);const a=Object.keys(n);for(let f=0;f<a.length;f++){const L=a[f],M=n[L];if((M==null?void 0:M.isTexture)===!0){const O=(h=(d=M.userData)==null?void 0:d.associations)==null?void 0:h.textures,_=r.parser.json.textures[O];if(!_){console.warn("Texture data not found for texture index "+O);continue}if((D=_==null?void 0:_.extensions)!=null&&D[R]){const k=_.extensions[R];k&&i&&S.registerTexture(i,M,k.lods.length,O,k)}}}};const s=t.material;if(Array.isArray(s))for(const n of s)o(n);else o(s)}}tryParseMeshLOD(e,t){var o,s;if(t[ne]==!0)return;t[ne]=!0;const r=this.getUrl();if(!r)return;const i=(s=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:s[R];if(i&&r){const n=t.uuid;S.registerMesh(r,n,t,0,i.lods.length,i)}}}function ve(l,e,t,r){te(e),re(t),t.register(o=>new S(o,l));const i=P.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=R;exports.LODsManager=P;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=xe;exports.setKTX2TranscoderLocation=me;exports.setRaycastMesh=le;exports.useNeedleProgressive=ve;
|
package/lib/extension.d.ts
CHANGED
|
@@ -10,7 +10,12 @@ declare type NEEDLE_progressive_model = {
|
|
|
10
10
|
guid: string;
|
|
11
11
|
lods: Array<NEEDLE_progressive_model_LOD>;
|
|
12
12
|
};
|
|
13
|
-
export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {
|
|
13
|
+
export declare type NEEDLE_progressive_texture_model = NEEDLE_progressive_model & {
|
|
14
|
+
lods: Array<NEEDLE_progressive_model_LOD & {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
14
19
|
export declare type NEEDLE_progressive_mesh_model = NEEDLE_progressive_model & {
|
|
15
20
|
density: number;
|
|
16
21
|
lods: Array<NEEDLE_progressive_model_LOD & {
|
|
@@ -33,6 +38,14 @@ export declare type ProgressiveMaterialTextureLoadingResult = {
|
|
|
33
38
|
/** the level of detail that was loaded */
|
|
34
39
|
level: number;
|
|
35
40
|
};
|
|
41
|
+
declare type TextureLODsMinMaxInfo = {
|
|
42
|
+
min_count: number;
|
|
43
|
+
max_count: number;
|
|
44
|
+
lods: Array<{
|
|
45
|
+
min_height: number;
|
|
46
|
+
max_height: number;
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
36
49
|
/**
|
|
37
50
|
* The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
|
|
38
51
|
* This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
|
|
@@ -52,13 +65,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
52
65
|
/** The name of the extension */
|
|
53
66
|
get name(): string;
|
|
54
67
|
static getMeshLODInformation(geo: BufferGeometry): NEEDLE_progressive_mesh_model | null;
|
|
55
|
-
static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?:
|
|
56
|
-
min: number;
|
|
57
|
-
max: number;
|
|
58
|
-
}): {
|
|
59
|
-
min: number;
|
|
60
|
-
max: number;
|
|
61
|
-
};
|
|
68
|
+
static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?: TextureLODsMinMaxInfo): TextureLODsMinMaxInfo;
|
|
62
69
|
/** Check if a LOD level is available for a mesh or a texture
|
|
63
70
|
* @param obj the mesh or texture to check
|
|
64
71
|
* @param level the level of detail to check for (0 is the highest resolution). If undefined, the function checks if any LOD level is available
|
|
@@ -98,7 +105,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
98
105
|
/**
|
|
99
106
|
* Register a texture with LOD information
|
|
100
107
|
*/
|
|
101
|
-
static registerTexture: (url: string, tex: Texture, index: number, ext: NEEDLE_progressive_texture_model) => void;
|
|
108
|
+
static registerTexture: (url: string, tex: Texture, level: number, index: number, ext: NEEDLE_progressive_texture_model) => void;
|
|
102
109
|
/**
|
|
103
110
|
* Register a mesh with LOD information
|
|
104
111
|
*/
|
package/lib/extension.js
CHANGED
|
@@ -95,28 +95,52 @@ export class NEEDLE_progressive {
|
|
|
95
95
|
}
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
|
-
static getMaterialMinMaxLODsCount(material, minmax
|
|
98
|
+
static getMaterialMinMaxLODsCount(material, minmax) {
|
|
99
|
+
// we can cache this material min max data because it wont change at runtime
|
|
100
|
+
const cacheKey = "LODS:minmax";
|
|
101
|
+
const cached = material[cacheKey];
|
|
102
|
+
if (cached != undefined)
|
|
103
|
+
return cached;
|
|
104
|
+
if (!minmax) {
|
|
105
|
+
minmax = {
|
|
106
|
+
min_count: Infinity,
|
|
107
|
+
max_count: 0,
|
|
108
|
+
lods: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
99
111
|
if (Array.isArray(material)) {
|
|
100
112
|
for (const mat of material) {
|
|
101
113
|
this.getMaterialMinMaxLODsCount(mat, minmax);
|
|
102
114
|
}
|
|
115
|
+
material[cacheKey] = minmax;
|
|
103
116
|
return minmax;
|
|
104
117
|
}
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
if (debug === "verbose")
|
|
119
|
+
console.log("getMaterialMinMaxLODsCount", material);
|
|
120
|
+
const processTexture = (tex) => {
|
|
121
|
+
const info = this.getAssignedLODInformation(tex);
|
|
122
|
+
if (info) {
|
|
123
|
+
const model = this.lodInfos.get(info.key);
|
|
124
|
+
if (model && model.lods) {
|
|
125
|
+
minmax.min_count = Math.min(minmax.min_count, model.lods.length);
|
|
126
|
+
minmax.max_count = Math.max(minmax.max_count, model.lods.length);
|
|
127
|
+
for (let i = 0; i < model.lods.length; i++) {
|
|
128
|
+
const lod = model.lods[i];
|
|
129
|
+
if (lod.width) {
|
|
130
|
+
minmax.lods[i] = minmax.lods[i] || { min_height: Infinity, max_height: 0 };
|
|
131
|
+
minmax.lods[i].min_height = Math.min(minmax.lods[i].min_height, lod.height);
|
|
132
|
+
minmax.lods[i].max_height = Math.max(minmax.lods[i].max_height, lod.height);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
107
138
|
if (material.type === "ShaderMaterial" || material.type === "RawShaderMaterial") {
|
|
108
139
|
const mat = material;
|
|
109
140
|
for (const slot of Object.keys(mat.uniforms)) {
|
|
110
141
|
const val = mat.uniforms[slot].value;
|
|
111
142
|
if (val?.isTexture === true) {
|
|
112
|
-
|
|
113
|
-
if (info) {
|
|
114
|
-
const model = this.lodInfos.get(info.key);
|
|
115
|
-
if (model && model.lods) {
|
|
116
|
-
min = Math.min(min, model.lods.length);
|
|
117
|
-
max = Math.max(max, model.lods.length);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
143
|
+
processTexture(val);
|
|
120
144
|
}
|
|
121
145
|
}
|
|
122
146
|
}
|
|
@@ -124,19 +148,11 @@ export class NEEDLE_progressive {
|
|
|
124
148
|
for (const slot of Object.keys(material)) {
|
|
125
149
|
const val = material[slot];
|
|
126
150
|
if (val?.isTexture === true) {
|
|
127
|
-
|
|
128
|
-
if (info) {
|
|
129
|
-
const model = this.lodInfos.get(info.key);
|
|
130
|
-
if (model && model.lods) {
|
|
131
|
-
min = Math.min(min, model.lods.length);
|
|
132
|
-
max = Math.max(max, model.lods.length);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
151
|
+
processTexture(val);
|
|
135
152
|
}
|
|
136
153
|
}
|
|
137
154
|
}
|
|
138
|
-
|
|
139
|
-
minmax.max = max;
|
|
155
|
+
material[cacheKey] = minmax;
|
|
140
156
|
return minmax;
|
|
141
157
|
}
|
|
142
158
|
/** Check if a LOD level is available for a mesh or a texture
|
|
@@ -317,29 +333,29 @@ export class NEEDLE_progressive {
|
|
|
317
333
|
static assignTextureLODForSlot(current, level, material, slot) {
|
|
318
334
|
if (current?.isTexture !== true)
|
|
319
335
|
return Promise.resolve(null);
|
|
320
|
-
// if (debug) console.log("-----------\n", "FIND", material?.name, slot, current?.name, current?.userData, current, material);
|
|
321
336
|
if (slot === "glyphMap") {
|
|
322
337
|
return Promise.resolve(current);
|
|
323
338
|
}
|
|
324
|
-
// save which texture level was requested last
|
|
325
|
-
const key = "LOD:requested level:" + slot;
|
|
326
|
-
if (material)
|
|
327
|
-
material[key] = level;
|
|
328
339
|
return NEEDLE_progressive.getOrLoadLOD(current, level).then(tex => {
|
|
329
|
-
// check if the requested level has changed in the meantime
|
|
330
|
-
if (material && material[key] != level)
|
|
331
|
-
return null;
|
|
332
|
-
// if (material) delete material[key];
|
|
333
340
|
// this can currently not happen
|
|
334
341
|
if (Array.isArray(tex))
|
|
335
342
|
return null;
|
|
336
343
|
if (tex?.isTexture === true) {
|
|
337
344
|
if (tex != current) {
|
|
338
|
-
// if (debug) console.warn("Assign LOD", material?.name, slot, tex.name, tex["guid"], material, "Prev:", current, "Now:", tex, "\n--------------");
|
|
339
|
-
// tex.needsUpdate = true;
|
|
340
345
|
if (material && slot) {
|
|
346
|
+
const assigned = material[slot];
|
|
347
|
+
// Check if the assigned texture LOD is higher quality than the current LOD
|
|
348
|
+
// This is necessary for cases where e.g. a texture is updated via an explicit call to assignTextureLOD
|
|
349
|
+
if (assigned) {
|
|
350
|
+
const assignedLOD = this.getAssignedLODInformation(assigned);
|
|
351
|
+
if (assignedLOD && assignedLOD?.level < level) {
|
|
352
|
+
if (debug === "verbose")
|
|
353
|
+
console.warn("Assigned texture level is already higher: ", assignedLOD.level, level, material, assigned, tex);
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
// assigned.dispose();
|
|
357
|
+
}
|
|
341
358
|
material[slot] = tex;
|
|
342
|
-
// material.needsUpdate = true;
|
|
343
359
|
}
|
|
344
360
|
if (debug && slot && material) {
|
|
345
361
|
const lodinfo = this.getAssignedLODInformation(current);
|
|
@@ -406,7 +422,7 @@ export class NEEDLE_progressive {
|
|
|
406
422
|
const val = this.parser.associations.get(key);
|
|
407
423
|
if (val.textures === index) {
|
|
408
424
|
found = true;
|
|
409
|
-
NEEDLE_progressive.registerTexture(this.url, key, index, ext);
|
|
425
|
+
NEEDLE_progressive.registerTexture(this.url, key, ext.lods.length, index, ext);
|
|
410
426
|
}
|
|
411
427
|
}
|
|
412
428
|
}
|
|
@@ -414,7 +430,7 @@ export class NEEDLE_progressive {
|
|
|
414
430
|
if (!found) {
|
|
415
431
|
this.parser.getDependency("texture", index).then(tex => {
|
|
416
432
|
if (tex) {
|
|
417
|
-
NEEDLE_progressive.registerTexture(this.url, tex, index, ext);
|
|
433
|
+
NEEDLE_progressive.registerTexture(this.url, tex, ext.lods.length, index, ext);
|
|
418
434
|
}
|
|
419
435
|
});
|
|
420
436
|
}
|
|
@@ -452,7 +468,7 @@ export class NEEDLE_progressive {
|
|
|
452
468
|
/**
|
|
453
469
|
* Register a texture with LOD information
|
|
454
470
|
*/
|
|
455
|
-
static registerTexture = (url, tex, index, ext) => {
|
|
471
|
+
static registerTexture = (url, tex, level, index, ext) => {
|
|
456
472
|
if (debug)
|
|
457
473
|
console.log("> Progressive: register texture", index, tex.name, tex.uuid, tex, ext);
|
|
458
474
|
// Put the extension info into the source (seems like tiled textures are cloned and the userdata etc is not properly copied BUT the source of course is not cloned)
|
|
@@ -460,7 +476,7 @@ export class NEEDLE_progressive {
|
|
|
460
476
|
if (tex.source)
|
|
461
477
|
tex.source[$progressiveTextureExtension] = ext;
|
|
462
478
|
const LODKEY = ext.guid;
|
|
463
|
-
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY,
|
|
479
|
+
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, level, index, undefined);
|
|
464
480
|
NEEDLE_progressive.lodInfos.set(LODKEY, ext);
|
|
465
481
|
NEEDLE_progressive.lowresCache.set(LODKEY, tex);
|
|
466
482
|
};
|
package/lib/lods_manager.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ export declare class LODsManager {
|
|
|
108
108
|
declare class LOD_state {
|
|
109
109
|
frames: number;
|
|
110
110
|
lastLodLevel_Mesh: number;
|
|
111
|
-
|
|
111
|
+
lastLodLevel_Texture: number;
|
|
112
112
|
lastScreenCoverage: number;
|
|
113
113
|
readonly lastScreenspaceVolume: Vector3;
|
|
114
114
|
lastCentrality: number;
|
package/lib/lods_manager.js
CHANGED
|
@@ -232,7 +232,7 @@ export class LODsManager {
|
|
|
232
232
|
plugin.onAfterUpdatedLOD?.(this.renderer, scene, camera, object, levels);
|
|
233
233
|
}
|
|
234
234
|
state.lastLodLevel_Mesh = levels.mesh_lod;
|
|
235
|
-
state.
|
|
235
|
+
state.lastLodLevel_Texture = levels.texture_lod;
|
|
236
236
|
}
|
|
237
237
|
/** Load progressive textures for the given material
|
|
238
238
|
* @param material the material to load the textures for
|
|
@@ -338,7 +338,7 @@ export class LODsManager {
|
|
|
338
338
|
const mesh_lods = mesh_lods_info?.lods;
|
|
339
339
|
const has_mesh_lods = mesh_lods && mesh_lods.length > 0;
|
|
340
340
|
const texture_lods_minmax = NEEDLE_progressive.getMaterialMinMaxLODsCount(mesh.material);
|
|
341
|
-
const has_texture_lods = texture_lods_minmax?.
|
|
341
|
+
const has_texture_lods = texture_lods_minmax?.min_count != Infinity && texture_lods_minmax.min_count > 0 && texture_lods_minmax.max_count > 0;
|
|
342
342
|
// We can skip all this if we dont have any LOD information
|
|
343
343
|
if (!has_mesh_lods && !has_texture_lods) {
|
|
344
344
|
result.mesh_lod = 0;
|
|
@@ -486,9 +486,34 @@ export class LODsManager {
|
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
488
|
result.mesh_lod = mesh_level;
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
|
|
489
|
+
if (has_texture_lods) {
|
|
490
|
+
// If this is the first time a texture LOD is requested we want to get the highest LOD to not display the minimal resolution that the root glTF contains as long while we wait for loading of e.g. the 8k LOD 0 texture
|
|
491
|
+
if (state.lastLodLevel_Texture < 0) {
|
|
492
|
+
result.texture_lod = texture_lods_minmax.max_count - 1;
|
|
493
|
+
if (debugProgressiveLoading) {
|
|
494
|
+
const level = texture_lods_minmax.lods[texture_lods_minmax.max_count - 1];
|
|
495
|
+
if (debugProgressiveLoading)
|
|
496
|
+
console.log(`First Texture LOD ${result.texture_lod} (${level.max_height}px) - ${mesh.name}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const factor = state.lastScreenCoverage * 1.5;
|
|
501
|
+
const screenSize = this.renderer.domElement.clientHeight / window.devicePixelRatio;
|
|
502
|
+
const pixelSizeOnScreen = screenSize * factor;
|
|
503
|
+
for (let i = texture_lods_minmax.lods.length - 1; i >= 0; i--) {
|
|
504
|
+
const lod = texture_lods_minmax.lods[i];
|
|
505
|
+
if (lod.max_height > pixelSizeOnScreen) {
|
|
506
|
+
result.texture_lod = i;
|
|
507
|
+
if (result.texture_lod < state.lastLodLevel_Texture) {
|
|
508
|
+
if (debugProgressiveLoading)
|
|
509
|
+
console.log(`Texture LOD changed ${state.lastLodLevel_Texture} → ${result.texture_lod} (${lod.max_height}px: ${(100 * state.lastScreenCoverage).toFixed(2)} % = ${pixelSizeOnScreen.toFixed(0)}px) - ${mesh.name}`);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// const t = Math.min(1, Math.max(0, state.lastScreenCoverage * 1.1));
|
|
515
|
+
// result.texture_lod = lerp(texture_lods_minmax.max_count, 0, t);
|
|
516
|
+
}
|
|
492
517
|
}
|
|
493
518
|
else {
|
|
494
519
|
result.texture_lod = 0;
|
|
@@ -500,8 +525,8 @@ function lerp(a, b, t) {
|
|
|
500
525
|
}
|
|
501
526
|
class LOD_state {
|
|
502
527
|
frames = 0;
|
|
503
|
-
lastLodLevel_Mesh =
|
|
504
|
-
|
|
528
|
+
lastLodLevel_Mesh = -1;
|
|
529
|
+
lastLodLevel_Texture = -1;
|
|
505
530
|
lastScreenCoverage = 0;
|
|
506
531
|
lastScreenspaceVolume = new Vector3();
|
|
507
532
|
lastCentrality = 0;
|
|
@@ -111,7 +111,7 @@ class RegisterModelviewerDataPlugin {
|
|
|
111
111
|
if (textureData?.extensions?.[EXTENSION_NAME]) {
|
|
112
112
|
const ext = textureData.extensions[EXTENSION_NAME];
|
|
113
113
|
if (ext && url) {
|
|
114
|
-
NEEDLE_progressive.registerTexture(url, value, ext.lods.length, ext);
|
|
114
|
+
NEEDLE_progressive.registerTexture(url, value, ext.lods.length, textureIndex, ext);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
}
|
package/package.json
CHANGED