@needle-tools/gltf-progressive 1.1.0-alpha.1 → 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 +8 -0
- package/gltf-progressive.js +320 -288
- package/gltf-progressive.min.js +4 -4
- package/gltf-progressive.umd.cjs +4 -4
- package/lib/extension.d.ts +18 -9
- package/lib/extension.js +81 -39
- 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 a;v&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const i=r.geometry;i.userData||(i.userData={}),M.assignLODInformation(t,i,e,o,n,s.density),M.lodInfos.set(e,s);let u=M.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],M.lowresCache.set(e,u),o>0&&!ne(r)&&ie(r,i);for(const l of C)(a=l.onRegisteredNewMesh)==null||a.call(l,r,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class be{constructor(e,r,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}const re=Z("debugprogressive"),Se=Z("noprogressive"),le=Symbol("Needle:LODSManager"),E={mesh_lod:-1,texture_lod:-1},S=class{constructor(t){c(this,"renderer"),c(this,"projectionScreenMatrix",new se),c(this,"cameraFrustrum",new De),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new ve),c(this,"_tempBox",new oe),c(this,"_tempBox2",new oe),c(this,"tempMatrix",new se),c(this,"_tempWorldPosition",new P),c(this,"_tempBoxSize",new P),c(this,"_tempBox2Size",new P),this.renderer=t}static getObjectLODState(t){var e;return(e=t.userData)==null?void 0:e.LOD_state}static addPlugin(t){C.push(t)}static removePlugin(t){const e=C.indexOf(t);e>=0&&C.splice(e,1)}static get(t){return t[le]?t[le]:new S(t)}get plugins(){return C}enable(){if(this._originalRender)return;let t=0;this._originalRender=this.renderer.render;const e=this;J(this.renderer),this.renderer.render=function(r,o){e.renderer.getRenderTarget()==null&&(t=0,e._frame+=1);const n=e._frame,s=t++;e.onBeforeRender(r,o,s,n),e._originalRender.call(this,r,o),e.onAfterRender(r,o,s,n)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,o){}onAfterRender(t,e,r,o){var n,s;if(this.pause)return;const a=this.renderer.renderLists.get(t,0),i=a.opaque;let u=!0;if(i.length===1){const l=i[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(u=!1)}if(e.parent&&e.parent.type==="CubeCamera"&&(u=!1),u){if(Se||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const l=this.targetTriangleDensity;for(const h of i){if(h.material&&(((n=h.geometry)==null?void 0:n.type)==="BoxGeometry"||((s=h.geometry)==null?void 0:s.type)==="BufferGeometry")&&(h.material.name==="SphericalGaussianBlur"||h.material.name=="BackgroundCubeMaterial"||h.material.name==="CubemapFromEquirect"||h.material.name==="EquirectangularToCubeUV")){re&&(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(h.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",h,h.material.name,h.material.type)));continue}switch(h.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}const x=a.transparent;for(const h of x){const f=h.object;(f instanceof q||f.isMesh)&&this.updateLODs(t,e,f,l,o)}}}updateLODs(t,e,r,o,n){var s,a;let i=r.userData.LOD_state;if(i||(i=new Te,r.userData.LOD_state=i),i.frames++<2)return;for(const l of C)(s=l.onBeforeUpdateLOD)==null||s.call(l,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,o,E),E.mesh_lod=Math.round(E.mesh_lod),E.texture_lod=Math.round(E.texture_lod),E.mesh_lod>=0&&this.loadProgressiveMeshes(r,E.mesh_lod);let u=E.texture_lod;if(r.material&&u>=0){const l=r["DEBUG:LOD"];l!=null&&(u=l),this.loadProgressiveTextures(r.material,u)}for(const l of C)(a=l.onAfterUpdatedLOD)==null||a.call(l,this.renderer,t,e,r,E);i.lastLodLevel_Mesh=E.mesh_lod,i.lasLodLevel_Texture=E.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}const r=t;let o=!1;(r.NEEDLE_LOD==null||e<r.NEEDLE_LOD)&&(o=!0),o&&(r.NEEDLE_LOD=e,w.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t.userData||(t.userData={}),t.userData.LOD!==e){t.userData.LOD=e;const r=t.geometry;return w.assignMeshLOD(t,e).then(o=>(o&&t.userData.LOD==e&&r!=t.geometry,o))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,o=t.max,n=(r.x+o.x)*.5,s=(r.y+o.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,o,n){var s;if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1;if(re&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=w.getMeshLODInformation(e.geometry),u=i?.lods,l=u&&u.length>0,x=w.getMaterialMinMaxLODsCount(e.material),h=x?.min!=1/0&&x.min>0&&x.max>0;if(!l&&!h){n.mesh_lod=0,n.texture_lod=0;return}if(l||(a=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){n.mesh_lod=99,n.texture_lod=99;return}const f=e.geometry.boundingBox;if(f&&t.isPerspectiveCamera){const b=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(f),this._tempBox.applyMatrix4(e.matrixWorld),S.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&b.fov>70){const g=this._tempBox.min,L=this._tempBox.max;let D=g.x,y=g.y,d=L.x,O=L.y;const T=2,B=1.5,j=(g.x+L.x)*.5,R=(g.y+L.y)*.5;D=(D-j)*T+j,y=(y-R)*T+R,d=(d-j)*T+j,O=(O-R)*T+R;const de=D<0&&d>0?0:Math.min(Math.abs(g.x),Math.abs(L.x)),he=y<0&&O>0?0:Math.min(Math.abs(g.y),Math.abs(L.y)),K=Math.max(de,he);r.lastCentrality=(B-K)*(B-K)*(B-K)}else r.lastCentrality=1;const m=this._tempBox.getSize(this._tempBoxSize);m.multiplyScalar(.5),screen.availHeight>0&&m.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),m.x*=b.aspect;const k=t.matrixWorldInverse,p=this._tempBox2;p.copy(f),p.applyMatrix4(e.matrixWorld),p.applyMatrix4(k);const _=p.getSize(this._tempBox2Size),G=Math.max(_.x,_.y);if(Math.max(m.x,m.y)!=0&&G!=0&&(m.z=_.z/Math.max(_.x,_.y)*Math.max(m.x,m.y)),r.lastScreenCoverage=Math.max(m.x,m.y,m.z),r.lastScreenspaceVolume.copy(m),r.lastScreenCoverage*=r.lastCentrality,re&&S.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const L=S.corner0,D=S.corner1,y=S.corner2,d=S.corner3;L.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=L.x,y.copy(this._tempBox.max),y.y=L.y,d.copy(this._tempBox.max);const O=(L.z+d.z)*.5;L.z=D.z=y.z=d.z=O,L.applyMatrix4(g),D.applyMatrix4(g),y.applyMatrix4(g),d.applyMatrix4(g),S.debugDrawLine(L,D,255),S.debugDrawLine(L,y,255),S.debugDrawLine(D,d,255),S.debugDrawLine(y,d,255)}let N=999;if(u&&r.lastScreenCoverage>0){for(let g=0;g<u.length;g++)if(u[g].density/r.lastScreenCoverage<o){N=g;break}}N<a&&(a=N)}if(n.mesh_lod=a,x!=null&&x.min&&x.min>0&&x.max>0){const b=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=Ee(x.max,0,b)}else n.texture_lod=0}};let A=S;c(A,"debugDrawLine"),c(A,"corner0",new P),c(A,"corner1",new P),c(A,"corner2",new P),c(A,"corner3",new P),c(A,"_tempPtInside",new P);function Ee(t,e,r){return t+(e-t)*r}class Te{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",0),c(this,"lasLodLevel_Texture",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new P),c(this,"lastCentrality",0)}}const ue=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(t){if(!t)return null;let e=null,r=null;for(let o=t;o!=null;o=Object.getPrototypeOf(o)){const n=Object.getOwnPropertySymbols(o),s=n.find(i=>i.toString()=="Symbol(renderer)"),a=n.find(i=>i.toString()=="Symbol(scene)");!e&&s!=null&&(e=t[s].threeRenderer),!r&&a!=null&&(r=t[a])}if(e){console.log("Adding Needle LODs to modelviewer");const o=A.get(e);if(A.addPlugin(new Ae(t)),o.enable(),r){const n=r.camera||r.traverse(s=>s.type=="PerspectiveCamera")[0];n&&e.render(r,n)}return()=>{o.disable()}}return null}class Ae{constructor(e){c(this,"modelviewer"),c(this,"_didWarnAboutMissingUrl",!1),this.modelviewer=e}onBeforeUpdateLOD(e,r,o,n){this.tryParseMeshLOD(r,n),this.tryParseTextureLOD(r,n)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,r){if(r[X]==!0)return;r[X]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(n&&o&&r.material){let s=function(i){var u,l,x;if(i[X]==!0)return;i[X]=!0,i.userData&&(i.userData.LOD=-1);const h=Object.keys(i);for(let f=0;f<h.length;f++){const b=h[f],m=i[b];if(m?.isTexture===!0){const k=(l=(u=m.userData)==null?void 0:u.associations)==null?void 0:l.textures,p=o.parser.json.textures[k];if(!p){console.warn("Texture data not found for texture index "+k);continue}if((x=p?.extensions)!=null&&x[I]){const _=p.extensions[I];_&&n&&w.registerTexture(n,m,_.lods.length,_)}}}};const a=r.material;if(Array.isArray(a))for(const i of a)s(i);else s(a)}}tryParseMeshLOD(e,r){var o,n;if(r[ue]==!0)return;r[ue]=!0;const s=this.getUrl();if(!s)return;const a=(n=(o=r.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[I];if(a&&s){const i=r.uuid;w.registerMesh(s,i,r,0,a.lods.length,a)}}}function Pe(t,e,r,o){J(e),Q(r),r.register(s=>new w(s,t));const n=A.get(e);return o?.enableLODsManager!==!1&&n.enable(),n}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});export{I as EXTENSION_NAME,A as LODsManager,w as NEEDLE_progressive,Q as addDracoAndKTX2Loaders,J as createLoaders,ne as getRaycastMesh,ce as patchModelViewer,Oe as setDracoDecoderLocation,we as setKTX2TranscoderLocation,ie as setRaycastMesh,Pe as useNeedleProgressive};
|
|
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=v;u(S,"registerTexture",(e,t,r,i)=>{_&&console.log("> Progressive: register texture",r,t.name,t.uuid,t,i),t.source&&(t.source[J]=i);const n=i.guid;v.assignLODInformation(e,t,n,0,0,void 0),v.lodInfos.set(n,i),v.lowresCache.set(n,t)}),u(S,"registerMesh",(e,t,r,i,n,s)=>{var d;_&&console.log("> Progressive: register mesh",n,r.name,s,r.uuid,r);const o=r.geometry;o.userData||(o.userData={}),v.assignLODInformation(e,o,t,i,n,s.density),v.lodInfos.set(t,s);let l=v.lowresCache.get(t);l?l.push(r.geometry):l=[r.geometry],v.lowresCache.set(t,l),i>0&&!ae(r)&&le(r,o);for(const h of R)(d=h.onRegisteredNewMesh)==null||d.call(h,r,s)}),u(S,"lodInfos",new Map),u(S,"previouslyLoaded",new Map),u(S,"lowresCache",new Map);class Me{constructor(e,t,r,i,n){u(this,"url");u(this,"key");u(this,"level");u(this,"index");u(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),n!=null&&(this.density=n)}}const Q=se("debugprogressive"),Oe=se("noprogressive"),ne=Symbol("Needle:LODSManager"),P={mesh_lod:-1,texture_lod:-1},A=class{constructor(e){u(this,"renderer");u(this,"projectionScreenMatrix",new g.Matrix4);u(this,"cameraFrustrum",new g.Frustum);u(this,"targetTriangleDensity",2e5);u(this,"updateInterval",0);u(this,"pause",!1);u(this,"_frame",0);u(this,"_originalRender");u(this,"_sphere",new g.Sphere);u(this,"_tempBox",new g.Box3);u(this,"_tempBox2",new g.Box3);u(this,"tempMatrix",new g.Matrix4);u(this,"_tempWorldPosition",new g.Vector3);u(this,"_tempBoxSize",new g.Vector3);u(this,"_tempBox2Size",new g.Vector3);this.renderer=e}static getObjectLODState(e){var t;return(t=e.userData)==null?void 0:t.LOD_state}static addPlugin(e){R.push(e)}static removePlugin(e){const t=R.indexOf(e);t>=0&&R.splice(t,1)}static get(e){return e[ne]?e[ne]:new A(e)}get plugins(){return R}enable(){if(this._originalRender)return;let e=0;this._originalRender=this.renderer.render;const t=this;te(this.renderer),this.renderer.render=function(r,i){t.renderer.getRenderTarget()==null&&(e=0,t._frame+=1);const s=t._frame,o=e++;t.onBeforeRender(r,i,o,s),t._originalRender.call(this,r,i),t.onAfterRender(r,i,o,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(e,t,r,i){}onAfterRender(e,t,r,i){var l,d;if(this.pause)return;const n=this.renderer.renderLists.get(e,0),s=n.opaque;let o=!0;if(s.length===1){const h=s[0].material;(h.name==="EffectMaterial"||h.name==="CopyShader")&&(o=!1)}if(t.parent&&t.parent.type==="CubeCamera"&&(o=!1),o){if(Oe||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const h=this.targetTriangleDensity;for(const f of s){if(f.material&&(((l=f.geometry)==null?void 0:l.type)==="BoxGeometry"||((d=f.geometry)==null?void 0:d.type)==="BufferGeometry")&&(f.material.name==="SphericalGaussianBlur"||f.material.name=="BackgroundCubeMaterial"||f.material.name==="CubemapFromEquirect"||f.material.name==="EquirectangularToCubeUV")){Q&&(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(f.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",f,f.material.name,f.material.type)));continue}switch(f.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}const L=n.transparent;for(const f of L){const m=f.object;(m instanceof g.Mesh||m.isMesh)&&this.updateLODs(e,t,m,h,i)}}}updateLODs(e,t,r,i,n){var l,d;let s=r.userData.LOD_state;if(s||(s=new _e,r.userData.LOD_state=s),s.frames++<2)return;for(const h of R)(l=h.onBeforeUpdateLOD)==null||l.call(h,this.renderer,e,t,r);this.calculateLodLevel(t,r,s,i,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod);let o=P.texture_lod;if(r.material&&o>=0){const h=r["DEBUG:LOD"];h!=null&&(o=h),this.loadProgressiveTextures(r.material,o)}for(const h of R)(d=h.onAfterUpdatedLOD)==null||d.call(h,this.renderer,e,t,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lasLodLevel_Texture=P.texture_lod}loadProgressiveTextures(e,t){if(!e)return;if(Array.isArray(e)){for(const n of e)this.loadProgressiveTextures(n,t);return}const r=e;let i=!1;(r.NEEDLE_LOD==null||t<r.NEEDLE_LOD)&&(i=!0),i&&(r.NEEDLE_LOD=t,S.assignTextureLOD(e,t))}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e.userData||(e.userData={}),e.userData.LOD!==t){e.userData.LOD=t;const r=e.geometry;return S.assignMeshLOD(e,t).then(i=>(i&&e.userData.LOD==t&&r!=e.geometry,i))}return Promise.resolve(null)}static isInside(e,t){const r=e.min,i=e.max,n=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(n,s,r.z).applyMatrix4(t).z<0}calculateLodLevel(e,t,r,i,n){var D;if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}let o=10+1;if(Q&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const l=S.getMeshLODInformation(t.geometry),d=l==null?void 0:l.lods,h=d&&d.length>0,L=S.getMaterialMinMaxLODsCount(t.material),f=(L==null?void 0:L.min)!=1/0&&L.min>0&&L.max>0;if(!h&&!f){n.mesh_lod=0,n.texture_lod=0;return}if(h||(o=0),!((D=this.cameraFrustrum)!=null&&D.intersectsObject(t))){n.mesh_lod=99,n.texture_lod=99;return}const m=t.geometry.boundingBox;if(m&&e.isPerspectiveCamera){const O=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const y=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(m),this._tempBox.applyMatrix4(t.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&O.fov>70){const y=this._tempBox.min,p=this._tempBox.max;let c=y.x,x=y.y,T=p.x,C=p.y;const I=2,N=1.5,V=(y.x+p.x)*.5,q=(y.y+p.y)*.5;c=(c-V)*I+V,x=(x-q)*I+q,T=(T-V)*I+V,C=(C-q)*I+q;const ue=c<0&&T>0?0:Math.min(Math.abs(y.x),Math.abs(p.x)),fe=x<0&&C>0?0:Math.min(Math.abs(y.y),Math.abs(p.y)),H=Math.max(ue,fe);r.lastCentrality=(N-H)*(N-H)*(N-H)}else r.lastCentrality=1;const M=this._tempBox.getSize(this._tempBoxSize);M.multiplyScalar(.5),screen.availHeight>0&&M.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),M.x*=O.aspect;const G=e.matrixWorldInverse,w=this._tempBox2;w.copy(m),w.applyMatrix4(t.matrixWorld),w.applyMatrix4(G);const E=w.getSize(this._tempBox2Size),F=Math.max(E.x,E.y);if(Math.max(M.x,M.y)!=0&&F!=0&&(M.z=E.z/Math.max(E.x,E.y)*Math.max(M.x,M.y)),r.lastScreenCoverage=Math.max(M.x,M.y,M.z),r.lastScreenspaceVolume.copy(M),r.lastScreenCoverage*=r.lastCentrality,Q&&A.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const p=A.corner0,c=A.corner1,x=A.corner2,T=A.corner3;p.copy(this._tempBox.min),c.copy(this._tempBox.max),c.x=p.x,x.copy(this._tempBox.max),x.y=p.y,T.copy(this._tempBox.max);const C=(p.z+T.z)*.5;p.z=c.z=x.z=T.z=C,p.applyMatrix4(y),c.applyMatrix4(y),x.applyMatrix4(y),T.applyMatrix4(y),A.debugDrawLine(p,c,255),A.debugDrawLine(p,x,255),A.debugDrawLine(c,T,255),A.debugDrawLine(x,T,255)}let B=999;if(d&&r.lastScreenCoverage>0){for(let y=0;y<d.length;y++)if(d[y].density/r.lastScreenCoverage<i){B=y;break}}B<o&&(o=B)}if(n.mesh_lod=o,L!=null&&L.min&&L.min>0&&L.max>0){const O=Math.min(1,Math.max(0,r.lastScreenCoverage*3));n.texture_lod=we(L.max,0,O)}else n.texture_lod=0}};let b=A;u(b,"debugDrawLine"),u(b,"corner0",new g.Vector3),u(b,"corner1",new g.Vector3),u(b,"corner2",new g.Vector3),u(b,"corner3",new g.Vector3),u(b,"_tempPtInside",new g.Vector3);function we(a,e,t){return a+(e-a)*t}class _e{constructor(){u(this,"frames",0);u(this,"lastLodLevel_Mesh",0);u(this,"lasLodLevel_Texture",0);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new g.Vector3);u(this,"lastCentrality",0)}}const oe=Symbol("NEEDLE_mesh_lod"),X=Symbol("NEEDLE_texture_lod");function ce(a){if(!a)return null;let e=null,t=null;for(let r=a;r!=null;r=Object.getPrototypeOf(r)){const i=Object.getOwnPropertySymbols(r),n=i.find(o=>o.toString()=="Symbol(renderer)"),s=i.find(o=>o.toString()=="Symbol(scene)");!e&&n!=null&&(e=a[n].threeRenderer),!t&&s!=null&&(t=a[s])}if(e){console.log("Adding Needle LODs to modelviewer");const r=b.get(e);if(b.addPlugin(new ve(a)),r.enable(),t){const i=t.camera||t.traverse(n=>n.type=="PerspectiveCamera")[0];i&&e.render(t,i)}return()=>{r.disable()}}return null}class ve{constructor(e){u(this,"modelviewer");u(this,"_didWarnAboutMissingUrl",!1);this.modelviewer=e}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(){let e=this.modelviewer.getAttribute("src");return e||(e=this.modelviewer.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",this.modelviewer),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[X]==!0)return;t[X]=!0;const r=this.tryGetCurrentGLTF(e),i=this.getUrl();if(i&&r&&t.material){let n=function(o){var d,h,L;if(o[X]==!0)return;o[X]=!0,o.userData&&(o.userData.LOD=-1);const l=Object.keys(o);for(let f=0;f<l.length;f++){const m=l[f],D=o[m];if((D==null?void 0:D.isTexture)===!0){const O=(h=(d=D.userData)==null?void 0:d.associations)==null?void 0:h.textures,M=r.parser.json.textures[O];if(!M){console.warn("Texture data not found for texture index "+O);continue}if((L=M==null?void 0:M.extensions)!=null&&L[k]){const G=M.extensions[k];G&&i&&S.registerTexture(i,D,G.lods.length,G)}}}};const s=t.material;if(Array.isArray(s))for(const o of s)n(o);else n(s)}}tryParseMeshLOD(e,t){var n,s;if(t[oe]==!0)return;t[oe]=!0;const r=this.getUrl();if(!r)return;const i=(s=(n=t.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[k];if(i&&r){const o=t.uuid;S.registerMesh(r,o,t,0,i.lods.length,i)}}}function Se(a,e,t,r){te(e),re(t),t.register(n=>new S(n,a));const i=b.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}document.addEventListener("DOMContentLoaded",()=>{ce(document.querySelector("model-viewer"))});exports.EXTENSION_NAME=k;exports.LODsManager=b;exports.NEEDLE_progressive=S;exports.addDracoAndKTX2Loaders=re;exports.createLoaders=te;exports.getRaycastMesh=ae;exports.patchModelViewer=ce;exports.setDracoDecoderLocation=Le;exports.setKTX2TranscoderLocation=xe;exports.setRaycastMesh=le;exports.useNeedleProgressive=Se;
|
|
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
|
|
@@ -92,11 +99,13 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
92
99
|
private readonly parser;
|
|
93
100
|
private readonly url;
|
|
94
101
|
constructor(parser: GLTFParser, url: string);
|
|
102
|
+
private _isLoadingMesh;
|
|
103
|
+
loadMesh: (meshIndex: number) => Promise<any> | null;
|
|
95
104
|
afterRoot(gltf: GLTF): null;
|
|
96
105
|
/**
|
|
97
106
|
* Register a texture with LOD information
|
|
98
107
|
*/
|
|
99
|
-
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;
|
|
100
109
|
/**
|
|
101
110
|
* Register a mesh with LOD information
|
|
102
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);
|
|
@@ -377,6 +393,22 @@ export class NEEDLE_progressive {
|
|
|
377
393
|
this.parser = parser;
|
|
378
394
|
this.url = url;
|
|
379
395
|
}
|
|
396
|
+
_isLoadingMesh;
|
|
397
|
+
loadMesh = (meshIndex) => {
|
|
398
|
+
if (this._isLoadingMesh)
|
|
399
|
+
return null;
|
|
400
|
+
const ext = this.parser.json.meshes[meshIndex]?.extensions?.[EXTENSION_NAME];
|
|
401
|
+
if (!ext)
|
|
402
|
+
return null;
|
|
403
|
+
this._isLoadingMesh = true;
|
|
404
|
+
return this.parser.getDependency("mesh", meshIndex).then(mesh => {
|
|
405
|
+
this._isLoadingMesh = false;
|
|
406
|
+
if (mesh) {
|
|
407
|
+
NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh, ext.lods.length, undefined, ext);
|
|
408
|
+
}
|
|
409
|
+
return mesh;
|
|
410
|
+
});
|
|
411
|
+
};
|
|
380
412
|
afterRoot(gltf) {
|
|
381
413
|
if (debug)
|
|
382
414
|
console.log("AFTER", this.url, gltf);
|
|
@@ -385,21 +417,20 @@ export class NEEDLE_progressive {
|
|
|
385
417
|
const ext = textureInfo?.extensions[EXTENSION_NAME];
|
|
386
418
|
if (ext) {
|
|
387
419
|
let found = false;
|
|
388
|
-
// TODO: why are sometimes textures not in the associations array when using r3f glTF loader...
|
|
389
420
|
for (const key of this.parser.associations.keys()) {
|
|
390
421
|
if (key.isTexture === true) {
|
|
391
422
|
const val = this.parser.associations.get(key);
|
|
392
423
|
if (val.textures === index) {
|
|
393
424
|
found = true;
|
|
394
|
-
NEEDLE_progressive.registerTexture(this.url, key, index, ext);
|
|
425
|
+
NEEDLE_progressive.registerTexture(this.url, key, ext.lods.length, index, ext);
|
|
395
426
|
}
|
|
396
427
|
}
|
|
397
428
|
}
|
|
429
|
+
// If textures aren't used there are no associations - we still want to register the LOD info so we create one instance
|
|
398
430
|
if (!found) {
|
|
399
|
-
// see TODO above, fallback for when textures are not in the associations array
|
|
400
431
|
this.parser.getDependency("texture", index).then(tex => {
|
|
401
432
|
if (tex) {
|
|
402
|
-
NEEDLE_progressive.registerTexture(this.url, tex, index, ext);
|
|
433
|
+
NEEDLE_progressive.registerTexture(this.url, tex, ext.lods.length, index, ext);
|
|
403
434
|
}
|
|
404
435
|
});
|
|
405
436
|
}
|
|
@@ -410,14 +441,25 @@ export class NEEDLE_progressive {
|
|
|
410
441
|
if (meshInfo?.extensions) {
|
|
411
442
|
const ext = meshInfo?.extensions[EXTENSION_NAME];
|
|
412
443
|
if (ext && ext.lods) {
|
|
444
|
+
let found = false;
|
|
413
445
|
for (const entry of this.parser.associations.keys()) {
|
|
414
446
|
if (entry.isMesh) {
|
|
415
447
|
const val = this.parser.associations.get(entry);
|
|
416
448
|
if (val.meshes === index) {
|
|
449
|
+
found = true;
|
|
417
450
|
NEEDLE_progressive.registerMesh(this.url, ext.guid, entry, ext.lods.length, val.primitives, ext);
|
|
418
451
|
}
|
|
419
452
|
}
|
|
420
453
|
}
|
|
454
|
+
// Note: we use loadMesh rather than this method so the mesh is surely registered at the right time when the mesh is created
|
|
455
|
+
// // If meshes aren't used there are no associations - we still want to register the LOD info so we create one instance
|
|
456
|
+
// if (!found) {
|
|
457
|
+
// this.parser.getDependency("mesh", index).then(mesh => {
|
|
458
|
+
// if (mesh) {
|
|
459
|
+
// NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh as Mesh, ext.lods.length, undefined, ext);
|
|
460
|
+
// }
|
|
461
|
+
// });
|
|
462
|
+
// }
|
|
421
463
|
}
|
|
422
464
|
}
|
|
423
465
|
});
|
|
@@ -426,7 +468,7 @@ export class NEEDLE_progressive {
|
|
|
426
468
|
/**
|
|
427
469
|
* Register a texture with LOD information
|
|
428
470
|
*/
|
|
429
|
-
static registerTexture = (url, tex, index, ext) => {
|
|
471
|
+
static registerTexture = (url, tex, level, index, ext) => {
|
|
430
472
|
if (debug)
|
|
431
473
|
console.log("> Progressive: register texture", index, tex.name, tex.uuid, tex, ext);
|
|
432
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)
|
|
@@ -434,7 +476,7 @@ export class NEEDLE_progressive {
|
|
|
434
476
|
if (tex.source)
|
|
435
477
|
tex.source[$progressiveTextureExtension] = ext;
|
|
436
478
|
const LODKEY = ext.guid;
|
|
437
|
-
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY,
|
|
479
|
+
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, level, index, undefined);
|
|
438
480
|
NEEDLE_progressive.lodInfos.set(LODKEY, ext);
|
|
439
481
|
NEEDLE_progressive.lowresCache.set(LODKEY, tex);
|
|
440
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;
|