@needle-tools/gltf-progressive 1.2.2-alpha.1 → 1.2.2-alpha.2
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 +2 -1
- package/examples/modelviewer-multiple.html +10 -0
- package/examples/modelviewer.html +4 -0
- package/gltf-progressive.js +336 -333
- package/gltf-progressive.min.js +6 -6
- package/gltf-progressive.umd.cjs +6 -6
- package/lib/lods_manager.js +4 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,
|
|
3
|
-
`,p),null));if(!
|
|
1
|
+
var we=Object.defineProperty,Oe=(t,e,r)=>e in t?we(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(Oe(t,typeof e!="symbol"?e+"":e,r),r);import{BufferGeometry as Z,Mesh as N,Material as be,Texture as z,TextureLoader as Se,Matrix4 as me,Frustum as Te,Sphere as Ee,Box3 as pe,Vector3 as R}from"three";import{GLTFLoader as Ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Ie}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Pe}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as Be}from"three/examples/jsm/loaders/KTX2Loader.js";const oe="";globalThis.GLTF_PROGRESSIVE_VERSION=oe,console.debug(`[gltf-progressive] version ${oe}`);let J="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ne="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(J+"draco_decoder.js",{method:"head"}).catch(t=>{J="./include/draco/",ne="./include/ktx2/"});function Ce(t){J=t}function Re(t){ne=t}let U,ie,V;function ae(t){U||(U=new Pe,U.setDecoderPath(J),U.setDecoderConfig({type:"js"})),V||(V=new Be,V.setTranscoderPath(ne)),ie||(ie=Ie),t?V.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function le(t){t.dracoLoader||t.setDRACOLoader(U),t.ktx2Loader||t.setKTX2Loader(V),t.meshoptDecoder||t.setMeshoptDecoder(ie)}q("debugprogressive");function q(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function ke(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const n=t.substring(0,r+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}let Q;function je(){return Q!==void 0||(Q=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),q("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",Q)),Q}const ue=Symbol("needle:raycast-mesh");function ee(t){return t?.[ue]instanceof Z?t[ue]:null}function xe(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!ee(t)){const r=$e(e);r.userData={isRaycastMesh:!0},t[ue]=r}}function Ge(t=!0){if(t){if(K)return;const e=K=N.prototype.raycast;N.prototype.raycast=function(r,n){const o=this,s=ee(o);let i;s&&o.isMesh&&(i=o.geometry,o.geometry=s),e.call(this,r,n),i&&(o.geometry=i)}}else{if(!K)return;N.prototype.raycast=K,K=null}}let K=null;function $e(t){const e=new Z;for(const r in t.attributes)e.setAttribute(r,t.getAttribute(r));return e.setIndex(t.getIndex()),e}const j=new Array,B="NEEDLE_progressive",y=q("debugprogressive"),ce=Symbol("needle-progressive-texture"),X=new Map,de=new Set;if(y){let t=function(){e+=1,console.log("Toggle LOD level",e,X),X.forEach((o,s)=>{for(const i of o.keys){const a=s[i];if(a!=null){if(a.isBufferGeometry===!0){const l=w.getMeshLODInformation(a),u=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=e,w.assignMeshLOD(s,u),l&&(r=Math.max(r,l.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.assignTextureLOD(s,e);break}}}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",o=>{o.key==="p"&&t(),o.key==="w"&&(n=!n,de&&de.forEach(s=>{s.name!="BackgroundCubeMaterial"&&s.glyphMap==null&&"wireframe"in s&&(s.wireframe=n)}))})}function ye(t,e,r){var n;if(!y)return;X.has(t)||X.set(t,{keys:[],sourceId:r});const o=X.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[B];return s?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>{var a;return this._isLoadingMesh=!1,i&&M.registerMesh(this.url,s.guid,i,(a=s.lods)==null?void 0:a.length,void 0,s),i})):null}),y&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return B}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const r=this,n="LODS:minmax",o=t[n];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const i of t)this.getMaterialMinMaxLODsCount(i,e);return t[n]=e,e}if(y==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const a of Object.keys(i.uniforms)){const l=i.uniforms[a].value;l?.isTexture===!0&&s(l,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const a=t[i];a?.isTexture===!0&&s(a,e)}return t[n]=e,e;function s(i,a){const l=r.getAssignedLODInformation(i);if(l){const u=r.lodInfos.get(l.key);if(u&&u.lods){a.min_count=Math.min(a.min_count,u.lods.length),a.max_count=Math.max(a.max_count,u.lods.length);for(let g=0;g<u.lods.length;g++){const h=u.lods[g];h.width&&(a.lods[g]=a.lods[g]||{min_height:1/0,max_height:0},a.lods[g].min_height=Math.min(a.lods[g].min_height,h.height),a.lods[g].max_height=Math.max(a.lods[g].max_height,h.height))}}}}}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 N||t.isMesh===!0){const n=t.geometry,o=this.getAssignedLODInformation(n);if(!o)return Promise.resolve(null);for(const s of j)(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?.isBufferGeometry?(t.geometry=s,y&&ye(t,"geometry",o.url)):y&&console.error("Invalid LOD geometry",s))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else y&&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 be||t.isMaterial===!0){const r=t,n=[],o=new Array;if(y&&de.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 l=this.assignTextureLODForSlot(a,e,r,i);n.push(l),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 l=s[a],u=o[a];l&&l.isTexture===!0?i.push({material:r,slot:u,texture:l,level:e}):i.push({material:r,slot:u,texture:null,level:e})}return i})}if(t instanceof z||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 y==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,s,o),null}r[n]=o}if(y&&n&&r){const s=this.getAssignedLODInformation(t);s&&ye(r,n,s.url)}}return o}else y=="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 y&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,o)=>{var s;if(n!=null&&n.extensions){const i=n?.extensions[B];if(i){if(!i.lods){y&&console.warn("Texture has no LODs",i);return}let a=!1;for(const l of this.parser.associations.keys())l.isTexture===!0&&this.parser.associations.get(l).textures===o&&(a=!0,M.registerTexture(this.url,l,(s=i.lods)==null?void 0:s.length,o,i));a||this.parser.getDependency("texture",o).then(l=>{var u;l&&M.registerTexture(this.url,l,(u=i.lods)==null?void 0:u.length,o,i)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,o)=>{if(n!=null&&n.extensions){const s=n?.extensions[B];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=y=="verbose",a=t.userData.LODS;if(!a)return null;const l=a?.key;let u;if(t.isTexture===!0){const g=t;g.source&&g.source[ce]&&(u=g.source[ce])}if(u||(u=M.lodInfos.get(l)),u){if(e>0){let f=!1;const S=Array.isArray(u.lods);if(S&&e>=u.lods.length?f=!0:S||(f=!0),f)return this.lowresCache.get(l)}const g=Array.isArray(u.lods)?(r=u.lods[e])==null?void 0:r.path:u.lods;if(!g)return y&&!u["missing:uri"]&&(u["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,u)),null;const h=ke(a.url,g);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!u.guid)return console.warn("missing pointer for glb/gltf texture",u),null;const f=h+"_"+u.guid,S=this.previouslyLoaded.get(f);if(S!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let m=await S.catch(b=>(console.error(`Error loading LOD ${e} from ${h}
|
|
2
|
+
`,b),null)),x=!1;if(m==null||(m instanceof z&&t instanceof z?(n=m.image)!=null&&n.data||(o=m.source)!=null&&o.data?m=this.copySettings(t,m):(x=!0,this.previouslyLoaded.delete(f)):m instanceof Z&&t instanceof Z&&((s=m.attributes.position)!=null&&s.array||(x=!0,this.previouslyLoaded.delete(f)))),!x)return m}const D=u,T=new Promise(async(m,x)=>{const b=new Ae;le(b),y&&(await new Promise(p=>setTimeout(p,1e3)),i&&console.warn("Start loading (delayed) "+h,D.guid));let E=h;if(D&&Array.isArray(D.lods)){const p=D.lods[e];p.hash&&(E+="?v="+p.hash)}const L=await b.loadAsync(E).catch(p=>(console.error(`Error loading LOD ${e} from ${h}
|
|
3
|
+
`,p),null));if(!L)return null;const F=L.parser;i&&console.log("Loading finished "+h,D.guid);let I=0;if(L.parser.json.textures){let p=!1;for(const d of L.parser.json.textures){if(d!=null&&d.extensions){const v=d?.extensions[B];if(v!=null&&v.guid&&v.guid===D.guid){p=!0;break}}I++}if(p){let d=await F.getDependency("texture",I);return d&&M.assignLODInformation(a.url,d,l,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',h,I,d,f),t instanceof z&&(d=this.copySettings(t,d)),d&&(d.guid=D.guid),m(d)}else y&&console.warn("Could not find texture with guid",D.guid,L.parser.json)}if(I=0,L.parser.json.meshes){let p=!1;for(const d of L.parser.json.meshes){if(d!=null&&d.extensions){const v=d?.extensions[B];if(v!=null&&v.guid&&v.guid===D.guid){p=!0;break}}I++}if(p){const d=await F.getDependency("mesh",I),v=D;if(i&&console.log(`Loaded Mesh "${d.name}"`,h,I,d,f),d.isMesh===!0){const _=d.geometry;return M.assignLODInformation(a.url,_,l,e,void 0,v.density),m(_)}else{const _=new Array;for(let O=0;O<d.children.length;O++){const k=d.children[O];if(k.isMesh===!0){const G=k.geometry;M.assignLODInformation(a.url,G,l,e,O,v.density),_.push(G)}}return m(_)}}else y&&console.warn("Could not find mesh with guid",D.guid,L.parser.json)}return m(null)});return this.previouslyLoaded.set(f,T),await T}else if(t instanceof z){i&&console.log("Load texture from uri: "+h);const f=await new Se().loadAsync(h);return f?(f.guid=u.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,i&&console.log(u,f)):y&&console.warn("failed loading",h),f}}else y&&console.warn(`Can not load LOD ${e}: no LOD info found for "${l}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,n,o,s){if(!e)return;e.userData||(e.userData={});const i=new Fe(t,r,n,o,s);e.userData.LODS=i}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),y&&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
|
|
6
|
-
Screensize: ${b.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${
|
|
7
|
-
${e.name}`)}break}}}else o.texture_lod=0}};let
|
|
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,n,o)=>{if(y&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,o),!e){y&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ce]=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(w,"registerMesh",(t,e,r,n,o,s)=>{var i;y&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const a=r.geometry;if(!a){y&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),M.assignLODInformation(t,a,e,n,o,s.density),M.lodInfos.set(e,s);let l=M.lowresCache.get(e);l?l.push(r.geometry):l=[r.geometry],M.lowresCache.set(e,l),n>0&&!ee(r)&&xe(r,a);for(const u of j)(i=u.onRegisteredNewMesh)==null||i.call(u,r,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class Fe{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 $=q("debugprogressive"),Ne=q("noprogressive"),he=Symbol("Needle:LODSManager"),fe=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1},A=class{constructor(t,e){c(this,"context"),c(this,"renderer"),c(this,"projectionScreenMatrix",new me),c(this,"cameraFrustrum",new Te),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"_frame",0),c(this,"_originalRender"),c(this,"_sphere",new Ee),c(this,"_tempBox",new pe),c(this,"_tempBox2",new pe),c(this,"tempMatrix",new me),c(this,"_tempWorldPosition",new R),c(this,"_tempBoxSize",new R),c(this,"_tempBox2Size",new R),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[fe]}static addPlugin(t){j.push(t)}static removePlugin(t){const e=j.indexOf(t);e>=0&&j.splice(e,1)}static get(t,e){if(t[he])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[he];const r=new A(t,{engine:"unknown",...e});return t[he]=r,r}get plugins(){return j}enable(){if(this._originalRender)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this._originalRender=this.renderer.render;const e=this;ae(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 l=!0;if(a.length===1){const u=a[0].material;(u.name==="EffectMaterial"||u.name==="CopyShader")&&(l=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(l=!1),l){if(Ne||this.updateInterval>0&&n%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const u=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")){$&&(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 N||f.isMesh)&&this.updateLODs(t,e,f,u,n)}const g=i.transparent;for(const h of g){const f=h.object;(f instanceof N||f.isMesh)&&this.updateLODs(t,e,f,u,n)}}}updateLODs(t,e,r,n,o){var s,i;r.userData||(r.userData={});let a=r[fe];if(a||(a=new We,r[fe]=a),a.frames++<2)return;for(const u of j)(s=u.onBeforeUpdateLOD)==null||s.call(u,this.renderer,t,e,r);this.calculateLodLevel(e,r,a,n,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 l=P.texture_lod;if(r.material&&l>=0){const u=r["DEBUG:LOD"];u!=null&&(l=u),this.loadProgressiveTextures(r.material,l)}for(const u of j)(i=u.onAfterUpdatedLOD)==null||i.call(u,this.renderer,t,e,r,P);a.lastLodLevel_Mesh=P.mesh_lod,a.lastLodLevel_Texture=P.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}let r=!1;(t[W]===void 0||e<t[W])&&(r=!0),r&&(t[W]=e,w.assignTextureLOD(t,e))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t[W]!==e){t[W]=e;const r=t.geometry;return w.assignMeshLOD(t,e).then(n=>(n&&t[W]==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,i;if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let a=10+1,l=!1;if($&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=w.getMeshLODInformation(e.geometry),g=u?.lods,h=g&&g.length>0,f=w.getMaterialMinMaxLODsCount(e.material),S=f?.min_count!=1/0&&f.min_count>0&&f.max_count>0;if(!h&&!S){o.mesh_lod=0,o.texture_lod=0;return}if(h||(l=!0,a=0),!((s=this.cameraFrustrum)!=null&&s.intersectsObject(e))){o.mesh_lod=99,o.texture_lod=99;return}const D=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let T=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;m.boundingBox||m.computeBoundingBox(),T=m.boundingBox}if(T&&t.isPerspectiveCamera){const m=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(T),this._tempBox.applyMatrix4(e.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.fov>70){const p=this._tempBox.min,d=this._tempBox.max;let v=p.x,_=p.y,O=d.x,k=d.y;const G=2,re=1.5,H=(p.x+d.x)*.5,Y=(p.y+d.y)*.5;v=(v-H)*G+H,_=(_-Y)*G+Y,O=(O-H)*G+H,k=(k-Y)*G+Y;const De=v<0&&O>0?0:Math.min(Math.abs(p.x),Math.abs(d.x)),_e=_<0&&k>0?0:Math.min(Math.abs(p.y),Math.abs(d.y)),se=Math.max(De,_e);r.lastCentrality=(re-se)*(re-se)*(re-se)}else r.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&D>0&&x.multiplyScalar(D/screen.availHeight),x.x*=m.aspect;const b=t.matrixWorldInverse,E=this._tempBox2;E.copy(T),E.applyMatrix4(e.matrixWorld),E.applyMatrix4(b);const L=E.getSize(this._tempBox2Size),F=Math.max(L.x,L.y);if(Math.max(x.x,x.y)!=0&&F!=0&&(x.z=L.z/Math.max(L.x,L.y)*Math.max(x.x,x.y)),r.lastScreenCoverage=Math.max(x.x,x.y,x.z),r.lastScreenspaceVolume.copy(x),r.lastScreenCoverage*=r.lastCentrality,$&&A.debugDrawLine){const p=this.tempMatrix.copy(this.projectionScreenMatrix);p.invert();const d=A.corner0,v=A.corner1,_=A.corner2,O=A.corner3;d.copy(this._tempBox.min),v.copy(this._tempBox.max),v.x=d.x,_.copy(this._tempBox.max),_.y=d.y,O.copy(this._tempBox.max);const k=(d.z+O.z)*.5;d.z=v.z=_.z=O.z=k,d.applyMatrix4(p),v.applyMatrix4(p),_.applyMatrix4(p),O.applyMatrix4(p),A.debugDrawLine(d,v,255),A.debugDrawLine(d,_,255),A.debugDrawLine(v,O,255),A.debugDrawLine(_,O,255)}let I=999;if(g&&r.lastScreenCoverage>0){for(let p=0;p<g.length;p++)if(g[p].density/r.lastScreenCoverage<n){I=p;break}}I<a&&(a=I,l=!0)}if(l?o.mesh_lod=a:o.mesh_lod=r.lastLodLevel_Mesh,$&&o.mesh_lod!=r.lastLodLevel_Mesh){const m=g?.[o.mesh_lod];m&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (${m.density.toFixed(0)}) - ${e.name}`)}if(S)if(r.lastLodLevel_Texture<0){if(o.texture_lod=f.max_count-1,$){const m=f.lods[f.max_count-1];$&&console.log(`First Texture LOD ${o.texture_lod} (${m.max_height}px) - ${e.name}`)}}else{const m=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let x=r.lastScreenCoverage*2;((i=this.context)==null?void 0:i.engine)==="model-viewer"&&(x*=2);const b=D/window.devicePixelRatio*x;for(let E=f.lods.length-1;E>=0;E--){let L=f.lods[E];if(!(je()&&L.max_height>4096)&&L.max_height>b){if(o.texture_lod=E,o.texture_lod<r.lastLodLevel_Texture){const F=L.max_height;$&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${F}px
|
|
6
|
+
Screensize: ${b.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${m.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}else o.texture_lod=0}};let C=A;c(C,"debugDrawLine"),c(C,"corner0",new R),c(C,"corner1",new R),c(C,"corner2",new R),c(C,"corner3",new R),c(C,"_tempPtInside",new R);class We{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",-1),c(this,"lastLodLevel_Texture",-1),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new R),c(this,"lastCentrality",0)}}const ve=Symbol("NEEDLE_mesh_lod"),te=Symbol("NEEDLE_texture_lod");function Le(){document.removeEventListener("DOMContentLoaded",ge),document.addEventListener("DOMContentLoaded",ge),ge()}function ge(){document.querySelectorAll("model-viewer").forEach((t,e)=>{ze(t,e)})}const Me=new WeakSet;function ze(t,e){if(!t||Me.has(t))return null;Me.add(t),console.debug("[gltf-progressive] found model-viewer..."+e);let r=null,n=null,o=null;for(let s=t;s!=null;s=Object.getPrototypeOf(s)){const i=Object.getOwnPropertySymbols(s),a=i.find(g=>g.toString()=="Symbol(renderer)"),l=i.find(g=>g.toString()=="Symbol(scene)"),u=i.find(g=>g.toString()=="Symbol(needsRender)");!r&&a!=null&&(r=t[a].threeRenderer),!n&&l!=null&&(n=t[l]),!o&&u!=null&&(o=t[u])}if(r&&n){console.debug("[gltf-progressive] setup model-viewer");const s=C.get(r,{engine:"model-viewer"});if(C.addPlugin(new Ue),s.enable(),n&&o){let i=0,a=setInterval(()=>{if(i++>10){clearInterval(a);return}o?.call(t)},150)}return()=>{s.disable()}}return null}class Ue{constructor(){c(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,r,n,o){this.tryParseMeshLOD(r,o),this.tryParseTextureLOD(r,o)}getUrl(e){if(!e)return null;let r=e.getAttribute("src");return r||(r=e.src),r||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),r}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,r){if(r[te]==!0)return;r[te]=!0;const n=this.tryGetCurrentGLTF(e),o=this.tryGetCurrentModelViewer(e),s=this.getUrl(o);if(s&&n&&r.material){let i=function(l){var u,g,h;if(l[te]==!0)return;l[te]=!0,l.userData&&(l.userData.LOD=-1);const f=Object.keys(l);for(let S=0;S<f.length;S++){const D=f[S],T=l[D];if(T?.isTexture===!0){const m=(g=(u=T.userData)==null?void 0:u.associations)==null?void 0:g.textures;if(m==null)continue;const x=n.parser.json.textures[m];if(!x){console.warn("Texture data not found for texture index "+m);continue}if((h=x?.extensions)!=null&&h[B]){const b=x.extensions[B];b&&s&&w.registerTexture(s,T,b.lods.length,m,b)}}}};const a=r.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(e,r){var n,o;if(r[ve]==!0)return;r[ve]=!0;const s=this.tryGetCurrentModelViewer(e),i=this.getUrl(s);if(!i)return;const a=(o=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:o[B];if(a&&i){const l=r.uuid;w.registerMesh(i,l,r,0,a.lods.length,a)}}}Le();function Ve(t,e,r,n){ae(e),le(r),r.register(s=>new w(s,t));const o=C.get(e);return n?.enableLODsManager!==!1&&o.enable(),o}export{B as EXTENSION_NAME,C as LODsManager,w as NEEDLE_progressive,oe as VERSION,le as addDracoAndKTX2Loaders,ae as createLoaders,ee as getRaycastMesh,Le as patchModelViewer,Ce as setDracoDecoderLocation,Re as setKTX2TranscoderLocation,xe as setRaycastMesh,Ve as useNeedleProgressive,Ge as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
1
|
+
"use strict";var _e=Object.defineProperty;var ve=(l,e,t)=>e in l?_e(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var d=(l,e,t)=>(ve(l,typeof e!="symbol"?e+"":e,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("three"),Oe=require("three/examples/jsm/loaders/GLTFLoader.js"),Se=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Te=require("three/examples/jsm/loaders/DRACOLoader.js"),be=require("three/examples/jsm/loaders/KTX2Loader.js"),de="";globalThis.GLTF_PROGRESSIVE_VERSION=de;console.debug(`[gltf-progressive] version ${de}`);let te="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",he="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(te+"draco_decoder.js",{method:"head"}).catch(l=>{te="./include/draco/",he="./include/ktx2/"});function Ae(l){te=l}function Pe(l){he=l}let X,ce,Y;function ge(l){X||(X=new Te.DRACOLoader,X.setDecoderPath(te),X.setDecoderConfig({type:"js"})),Y||(Y=new be.KTX2Loader,Y.setTranscoderPath(he)),ce||(ce=Se.MeshoptDecoder),l?Y.detectSupport(l):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function pe(l){l.dracoLoader||l.setDRACOLoader(X),l.ktx2Loader||l.setKTX2Loader(Y),l.meshoptDecoder||l.setMeshoptDecoder(ce)}J("debugprogressive");function J(l){const t=new URL(window.location.href).searchParams.get(l);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function Ce(l,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||l===void 0)return e;const t=l.lastIndexOf("/");if(t>=0){const r=l.substring(0,t+1);for(;r.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return r+e}return e}let q;function Re(){return q!==void 0||(q=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),J("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",q)),q}const ue=Symbol("needle:raycast-mesh");function re(l){return(l==null?void 0:l[ue])instanceof g.BufferGeometry?l[ue]:null}function Le(l,e){if((l.type==="Mesh"||l.type==="SkinnedMesh")&&!re(l)){const r=Be(e);r.userData={isRaycastMesh:!0},l[ue]=r}}function Ee(l=!0){if(l){if(K)return;const e=K=g.Mesh.prototype.raycast;g.Mesh.prototype.raycast=function(t,r){const i=this,o=re(i);let s;o&&i.isMesh&&(s=i.geometry,i.geometry=o),e.call(this,t,r),s&&(i.geometry=s)}}else{if(!K)return;g.Mesh.prototype.raycast=K,K=null}}let K=null;function Be(l){const e=new g.BufferGeometry;for(const t in l.attributes)e.setAttribute(t,l.getAttribute(t));return e.setIndex(l.getIndex()),e}const V=new Array,G="NEEDLE_progressive",m=J("debugprogressive"),ie=Symbol("needle-progressive-texture"),H=new Map,fe=new Set;if(m){let l=function(){e+=1,console.log("Toggle LOD level",e,H),H.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n!=null){if(n.isBufferGeometry===!0){const a=S.getMeshLODInformation(n),f=a?Math.min(e,a.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,f),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,fe&&fe.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=r)}))})}function ye(l,e,t){var i;if(!m)return;H.has(l)||H.set(l,{keys:[],sourceId:t});const r=H.get(l);((i=r==null?void 0:r.keys)==null?void 0:i.includes(e))==!1&&r.keys.push(e)}const O=class{constructor(e,t){d(this,"parser");d(this,"url");d(this,"_isLoadingMesh");d(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[G];return t?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",e).then(o=>{var s;return this._isLoadingMesh=!1,o&&O.registerMesh(this.url,t.guid,o,(s=t.lods)==null?void 0:s.length,void 0,t),o})):null});m&&console.log("Progressive extension registered for",t),this.parser=e,this.url=t}get name(){return G}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=this,i="LODS:minmax",o=e[i];if(o!=null)return o;if(t||(t={min_count:1/0,max_count:0,lods:[]}),Array.isArray(e)){for(const n of e)this.getMaterialMinMaxLODsCount(n,t);return e[i]=t,t}if(m==="verbose"&&console.log("getMaterialMinMaxLODsCount",e),e.type==="ShaderMaterial"||e.type==="RawShaderMaterial"){const n=e;for(const a of Object.keys(n.uniforms)){const f=n.uniforms[a].value;(f==null?void 0:f.isTexture)===!0&&s(f,t)}}else if(e.isMaterial)for(const n of Object.keys(e)){const a=e[n];(a==null?void 0:a.isTexture)===!0&&s(a,t)}return e[i]=t,t;function s(n,a){const f=r.getAssignedLODInformation(n);if(f){const c=r.lodInfos.get(f.key);if(c&&c.lods){a.min_count=Math.min(a.min_count,c.lods.length),a.max_count=Math.max(a.max_count,c.lods.length);for(let L=0;L<c.lods.length;L++){const u=c.lods[L];u.width&&(a.lods[L]=a.lods[L]||{min_height:1/0,max_height:0},a.lods[L].min_height=Math.min(a.lods[L].min_height,u.height),a.lods[L].max_height=Math.max(a.lods[L].max_height,u.height))}}}}}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 V)(r=s.onBeforeGetLODMesh)==null||r.call(s,e,t);return e["LOD:requested level"]=t,O.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==null?void 0:s.isBufferGeometry)?(e.geometry=s,m&&ye(e,"geometry",o.url)):m&&console.error("Invalid LOD geometry",s))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else m&&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(m&&fe.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 f=this.assignTextureLODForSlot(a,t,r,n);i.push(f),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 f=s[a],c=o[a];f&&f.isTexture===!0?n.push({material:r,slot:c,texture:f,level:t}):n.push({material:r,slot:c,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):O.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 m==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,t,r,s,o),null}r[i]=o}if(m&&i&&r){const s=this.getAssignedLODInformation(e);s&&ye(r,i,s.url)}}return o}else m=="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 m&&console.log("AFTER",this.url,e),(t=this.parser.json.textures)==null||t.forEach((i,o)=>{var s;if(i!=null&&i.extensions){const n=i==null?void 0:i.extensions[G];if(n){if(!n.lods){m&&console.warn("Texture has no LODs",n);return}let a=!1;for(const f of this.parser.associations.keys())f.isTexture===!0&&this.parser.associations.get(f).textures===o&&(a=!0,O.registerTexture(this.url,f,(s=n.lods)==null?void 0:s.length,o,n));a||this.parser.getDependency("texture",o).then(f=>{var c;f&&O.registerTexture(this.url,f,(c=n.lods)==null?void 0:c.length,o,n)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[G];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&&O.registerMesh(this.url,s.guid,n,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(e,t){var n,a,f,c;const r=m=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const L=e;L.source&&L.source[ie]&&(s=L.source[ie])}if(s||(s=O.lodInfos.get(o)),s){if(t>0){let x=!1;const _=Array.isArray(s.lods);if(_&&t>=s.lods.length?x=!0:_||(x=!0),x)return this.lowresCache.get(o)}const L=Array.isArray(s.lods)?(n=s.lods[t])==null?void 0:n.path:s.lods;if(!L)return m&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const u=Ce(i.url,L);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const x=u+"_"+s.guid,_=this.previouslyLoaded.get(x);if(_!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${x}`);let y=await _.catch(N=>(console.error(`Error loading LOD ${t} from ${u}
|
|
2
|
+
`,N),null)),D=!1;if(y==null||(y instanceof g.Texture&&e instanceof g.Texture?(a=y.image)!=null&&a.data||(f=y.source)!=null&&f.data?y=this.copySettings(e,y):(D=!0,this.previouslyLoaded.delete(x)):y instanceof g.BufferGeometry&&e instanceof g.BufferGeometry&&((c=y.attributes.position)!=null&&c.array||(D=!0,this.previouslyLoaded.delete(x)))),!D)return y}const M=s,B=new Promise(async(y,D)=>{const N=new Oe.GLTFLoader;pe(N),m&&(await new Promise(T=>setTimeout(T,1e3)),r&&console.warn("Start loading (delayed) "+u,M.guid));let k=u;if(M&&Array.isArray(M.lods)){const T=M.lods[t];T.hash&&(k+="?v="+T.hash)}const v=await N.loadAsync(k).catch(T=>(console.error(`Error loading LOD ${t} from ${u}
|
|
3
|
+
`,T),null));if(!v)return null;const $=v.parser;r&&console.log("Loading finished "+u,M.guid);let I=0;if(v.parser.json.textures){let T=!1;for(const p of v.parser.json.textures){if(p!=null&&p.extensions){const h=p==null?void 0:p.extensions[G];if(h!=null&&h.guid&&h.guid===M.guid){T=!0;break}}I++}if(T){let p=await $.getDependency("texture",I);return p&&O.assignLODInformation(i.url,p,o,t,void 0,void 0),r&&console.log('change "'+e.name+'" → "'+p.name+'"',u,I,p,x),e instanceof g.Texture&&(p=this.copySettings(e,p)),p&&(p.guid=M.guid),y(p)}else m&&console.warn("Could not find texture with guid",M.guid,v.parser.json)}if(I=0,v.parser.json.meshes){let T=!1;for(const p of v.parser.json.meshes){if(p!=null&&p.extensions){const h=p==null?void 0:p.extensions[G];if(h!=null&&h.guid&&h.guid===M.guid){T=!0;break}}I++}if(T){const p=await $.getDependency("mesh",I),h=M;if(r&&console.log(`Loaded Mesh "${p.name}"`,u,I,p,x),p.isMesh===!0){const w=p.geometry;return O.assignLODInformation(i.url,w,o,t,void 0,h.density),y(w)}else{const w=new Array;for(let b=0;b<p.children.length;b++){const A=p.children[b];if(A.isMesh===!0){const P=A.geometry;O.assignLODInformation(i.url,P,o,t,b,h.density),w.push(P)}}return y(w)}}else m&&console.warn("Could not find mesh with guid",M.guid,v.parser.json)}return y(null)});return this.previouslyLoaded.set(x,B),await B}else if(e instanceof g.Texture){r&&console.log("Load texture from uri: "+u);const _=await new g.TextureLoader().loadAsync(u);return _?(_.guid=s.guid,_.flipY=!1,_.needsUpdate=!0,_.colorSpace=e.colorSpace,r&&console.log(s,_)):m&&console.warn("failed loading",u),_}}else m&&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 ke(e,r,i,o,s);t.userData.LODS=n}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(),m&&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
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${t.name}`)}break}}}else o.texture_lod=0}};let
|
|
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=O;d(S,"registerTexture",(e,t,r,i,o)=>{if(m&&console.log("> Progressive: register texture",i,t.name,t.uuid,t,o),!t){m&&console.error("gltf-progressive: Register texture without texture");return}t.source&&(t.source[ie]=o);const s=o.guid;O.assignLODInformation(e,t,s,r,i,void 0),O.lodInfos.set(s,o),O.lowresCache.set(s,t)}),d(S,"registerMesh",(e,t,r,i,o,s)=>{var f;m&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;if(!n){m&&console.warn("gltf-progressive: Register mesh without geometry");return}n.userData||(n.userData={}),O.assignLODInformation(e,n,t,i,o,s.density),O.lodInfos.set(t,s);let a=O.lowresCache.get(t);a?a.push(r.geometry):a=[r.geometry],O.lowresCache.set(t,a),i>0&&!re(r)&&Le(r,n);for(const c of V)(f=c.onRegisteredNewMesh)==null||f.call(c,r,s)}),d(S,"lodInfos",new Map),d(S,"previouslyLoaded",new Map),d(S,"lowresCache",new Map);class ke{constructor(e,t,r,i,o){d(this,"url");d(this,"key");d(this,"level");d(this,"index");d(this,"density");this.url=e,this.key=t,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const z=J("debugprogressive"),Ie=J("noprogressive"),ne=Symbol("Needle:LODSManager"),ae=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),E={mesh_lod:-1,texture_lod:-1},C=class{constructor(e,t){d(this,"context");d(this,"renderer");d(this,"projectionScreenMatrix",new g.Matrix4);d(this,"cameraFrustrum",new g.Frustum);d(this,"targetTriangleDensity",2e5);d(this,"updateInterval",0);d(this,"pause",!1);d(this,"_frame",0);d(this,"_originalRender");d(this,"_sphere",new g.Sphere);d(this,"_tempBox",new g.Box3);d(this,"_tempBox2",new g.Box3);d(this,"tempMatrix",new g.Matrix4);d(this,"_tempWorldPosition",new g.Vector3);d(this,"_tempBoxSize",new g.Vector3);d(this,"_tempBox2Size",new g.Vector3);this.renderer=e,this.context={...t}}static getObjectLODState(e){return e[ae]}static addPlugin(e){V.push(e)}static removePlugin(e){const t=V.indexOf(e);t>=0&&V.splice(t,1)}static get(e,t){if(e[ne])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),e[ne];const r=new C(e,{engine:"unknown",...t});return e[ne]=r,r}get plugins(){return V}enable(){if(this._originalRender)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let e=0;this._originalRender=this.renderer.render;const t=this;ge(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,f;if(this.pause)return;const o=this.renderer.renderLists.get(e,0),s=o.opaque;let n=!0;if(s.length===1){const c=s[0].material;(c.name==="EffectMaterial"||c.name==="CopyShader")&&(n=!1)}if((t.parent&&t.parent.type==="CubeCamera"||r>=1&&t.type==="OrthographicCamera")&&(n=!1),n){if(Ie||this.updateInterval>0&&i%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const c=this.targetTriangleDensity;for(const u of s){if(u.material&&(((a=u.geometry)==null?void 0:a.type)==="BoxGeometry"||((f=u.geometry)==null?void 0:f.type)==="BufferGeometry")&&(u.material.name==="SphericalGaussianBlur"||u.material.name=="BackgroundCubeMaterial"||u.material.name==="CubemapFromEquirect"||u.material.name==="EquirectangularToCubeUV")){z&&(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",u,u.material.name,u.material.type)));continue}switch(u.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const x=u.object;(x instanceof g.Mesh||x.isMesh)&&this.updateLODs(e,t,x,c,i)}const L=o.transparent;for(const u of L){const x=u.object;(x instanceof g.Mesh||x.isMesh)&&this.updateLODs(e,t,x,c,i)}}}updateLODs(e,t,r,i,o){var a,f;r.userData||(r.userData={});let s=r[ae];if(s||(s=new Ge,r[ae]=s),s.frames++<2)return;for(const c of V)(a=c.onBeforeUpdateLOD)==null||a.call(c,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 c=r["DEBUG:LOD"];c!=null&&(n=c),this.loadProgressiveTextures(r.material,n)}for(const c of V)(f=c.onAfterUpdatedLOD)==null||f.call(c,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 i of e)this.loadProgressiveTextures(i,t);return}let r=!1;(e[U]===void 0||t<e[U])&&(r=!0),r&&(e[U]=t,S.assignTextureLOD(e,t))}loadProgressiveMeshes(e,t){if(!e)return Promise.resolve(null);if(e[U]!==t){e[U]=t;const r=e.geometry;return S.assignMeshLOD(e,t).then(i=>(i&&e[U]==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 B,F;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,a=!1;if(z&&t["DEBUG:LOD"]!=null)return t["DEBUG:LOD"];const f=S.getMeshLODInformation(t.geometry),c=f==null?void 0:f.lods,L=c&&c.length>0,u=S.getMaterialMinMaxLODsCount(t.material),x=(u==null?void 0:u.min_count)!=1/0&&u.min_count>0&&u.max_count>0;if(!L&&!x){o.mesh_lod=0,o.texture_lod=0;return}if(L||(a=!0,n=0),!((B=this.cameraFrustrum)!=null&&B.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const _=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=t.geometry.boundingBox;if(t.type==="SkinnedMesh"){const y=t;y.boundingBox||y.computeBoundingBox(),M=y.boundingBox}if(M&&e.isPerspectiveCamera){const y=e;if(t.geometry.attributes.color&&t.geometry.attributes.color.count<100&&t.geometry.boundingSphere){this._sphere.copy(t.geometry.boundingSphere),this._sphere.applyMatrix4(t.matrixWorld);const h=e.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(M),this._tempBox.applyMatrix4(t.matrixWorld),C.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&y.fov>70){const h=this._tempBox.min,w=this._tempBox.max;let b=h.x,A=h.y,P=w.x,W=w.y;const Q=2,se=1.5,Z=(h.x+w.x)*.5,j=(h.y+w.y)*.5;b=(b-Z)*Q+Z,A=(A-j)*Q+j,P=(P-Z)*Q+Z,W=(W-j)*Q+j;const De=b<0&&P>0?0:Math.min(Math.abs(h.x),Math.abs(w.x)),we=A<0&&W>0?0:Math.min(Math.abs(h.y),Math.abs(w.y)),oe=Math.max(De,we);r.lastCentrality=(se-oe)*(se-oe)*(se-oe)}else r.lastCentrality=1;const D=this._tempBox.getSize(this._tempBoxSize);D.multiplyScalar(.5),screen.availHeight>0&&_>0&&D.multiplyScalar(_/screen.availHeight),D.x*=y.aspect;const N=e.matrixWorldInverse,k=this._tempBox2;k.copy(M),k.applyMatrix4(t.matrixWorld),k.applyMatrix4(N);const v=k.getSize(this._tempBox2Size),$=Math.max(v.x,v.y);if(Math.max(D.x,D.y)!=0&&$!=0&&(D.z=v.z/Math.max(v.x,v.y)*Math.max(D.x,D.y)),r.lastScreenCoverage=Math.max(D.x,D.y,D.z),r.lastScreenspaceVolume.copy(D),r.lastScreenCoverage*=r.lastCentrality,z&&C.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const w=C.corner0,b=C.corner1,A=C.corner2,P=C.corner3;w.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=w.x,A.copy(this._tempBox.max),A.y=w.y,P.copy(this._tempBox.max);const W=(w.z+P.z)*.5;w.z=b.z=A.z=P.z=W,w.applyMatrix4(h),b.applyMatrix4(h),A.applyMatrix4(h),P.applyMatrix4(h),C.debugDrawLine(w,b,255),C.debugDrawLine(w,A,255),C.debugDrawLine(b,P,255),C.debugDrawLine(A,P,255)}let T=999;if(c&&r.lastScreenCoverage>0){for(let h=0;h<c.length;h++)if(c[h].density/r.lastScreenCoverage<i){T=h;break}}T<n&&(n=T,a=!0)}if(a?o.mesh_lod=n:o.mesh_lod=r.lastLodLevel_Mesh,z&&o.mesh_lod!=r.lastLodLevel_Mesh){const D=c==null?void 0:c[o.mesh_lod];D&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${o.mesh_lod} (${D.density.toFixed(0)}) - ${t.name}`)}if(x)if(r.lastLodLevel_Texture<0){if(o.texture_lod=u.max_count-1,z){const y=u.lods[u.max_count-1];z&&console.log(`First Texture LOD ${o.texture_lod} (${y.max_height}px) - ${t.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let D=r.lastScreenCoverage*2;((F=this.context)==null?void 0:F.engine)==="model-viewer"&&(D*=2);const k=_/window.devicePixelRatio*D;for(let v=u.lods.length-1;v>=0;v--){let $=u.lods[v];if(!(Re()&&$.max_height>4096)&&$.max_height>k){if(o.texture_lod=v,o.texture_lod<r.lastLodLevel_Texture){const I=$.max_height;z&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${o.texture_lod} = ${I}px
|
|
6
|
+
Screensize: ${k.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
|
|
7
|
+
${t.name}`)}break}}}else o.texture_lod=0}};let R=C;d(R,"debugDrawLine"),d(R,"corner0",new g.Vector3),d(R,"corner1",new g.Vector3),d(R,"corner2",new g.Vector3),d(R,"corner3",new g.Vector3),d(R,"_tempPtInside",new g.Vector3);class Ge{constructor(){d(this,"frames",0);d(this,"lastLodLevel_Mesh",-1);d(this,"lastLodLevel_Texture",-1);d(this,"lastScreenCoverage",0);d(this,"lastScreenspaceVolume",new g.Vector3);d(this,"lastCentrality",0)}}const xe=Symbol("NEEDLE_mesh_lod"),ee=Symbol("NEEDLE_texture_lod");function Me(){document.removeEventListener("DOMContentLoaded",le),document.addEventListener("DOMContentLoaded",le),le()}function le(){document.querySelectorAll("model-viewer").forEach((e,t)=>{Fe(e,t)})}const me=new WeakSet;function Fe(l,e){if(!l||me.has(l))return null;me.add(l),console.debug("[gltf-progressive] found model-viewer..."+e);let t=null,r=null,i=null;for(let o=l;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(c=>c.toString()=="Symbol(renderer)"),a=s.find(c=>c.toString()=="Symbol(scene)"),f=s.find(c=>c.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=l[n].threeRenderer),!r&&a!=null&&(r=l[a]),!i&&f!=null&&(i=l[f])}if(t&&r){console.debug("[gltf-progressive] setup model-viewer");const o=R.get(t,{engine:"model-viewer"});if(R.addPlugin(new $e),o.enable(),r&&i){let s=0,n=setInterval(()=>{if(s++>10){clearInterval(n);return}i==null||i.call(l)},150)}return()=>{o.disable()}}return null}class $e{constructor(){d(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,t,r,i){this.tryParseMeshLOD(t,i),this.tryParseTextureLOD(t,i)}getUrl(e){if(!e)return null;let t=e.getAttribute("src");return t||(t=e.src),t||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),t}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,t){if(t[ee]==!0)return;t[ee]=!0;const r=this.tryGetCurrentGLTF(e),i=this.tryGetCurrentModelViewer(e),o=this.getUrl(i);if(o&&r&&t.material){let s=function(a){var c,L,u;if(a[ee]==!0)return;a[ee]=!0,a.userData&&(a.userData.LOD=-1);const f=Object.keys(a);for(let x=0;x<f.length;x++){const _=f[x],M=a[_];if((M==null?void 0:M.isTexture)===!0){const B=(L=(c=M.userData)==null?void 0:c.associations)==null?void 0:L.textures;if(B==null)continue;const F=r.parser.json.textures[B];if(!F){console.warn("Texture data not found for texture index "+B);continue}if((u=F==null?void 0:F.extensions)!=null&&u[G]){const y=F.extensions[G];y&&o&&S.registerTexture(o,M,y.lods.length,B,y)}}}};const n=t.material;if(Array.isArray(n))for(const a of n)s(a);else s(n)}}tryParseMeshLOD(e,t){var s,n;if(t[xe]==!0)return;t[xe]=!0;const r=this.tryGetCurrentModelViewer(e),i=this.getUrl(r);if(!i)return;const o=(n=(s=t.userData)==null?void 0:s.gltfExtensions)==null?void 0:n[G];if(o&&i){const a=t.uuid;S.registerMesh(i,a,t,0,o.lods.length,o)}}}Me();function Ve(l,e,t,r){ge(e),pe(t),t.register(o=>new S(o,l));const i=R.get(e);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}exports.EXTENSION_NAME=G;exports.LODsManager=R;exports.NEEDLE_progressive=S;exports.VERSION=de;exports.addDracoAndKTX2Loaders=pe;exports.createLoaders=ge;exports.getRaycastMesh=re;exports.patchModelViewer=Me;exports.setDracoDecoderLocation=Ae;exports.setKTX2TranscoderLocation=Pe;exports.setRaycastMesh=Le;exports.useNeedleProgressive=Ve;exports.useRaycastMeshes=Ee;
|
package/lib/lods_manager.js
CHANGED
|
@@ -535,7 +535,10 @@ export class LODsManager {
|
|
|
535
535
|
}
|
|
536
536
|
else {
|
|
537
537
|
const volume = state.lastScreenspaceVolume.x + state.lastScreenspaceVolume.y + state.lastScreenspaceVolume.z;
|
|
538
|
-
|
|
538
|
+
let factor = state.lastScreenCoverage * 2;
|
|
539
|
+
if (this.context?.engine === "model-viewer") {
|
|
540
|
+
factor *= 2;
|
|
541
|
+
}
|
|
539
542
|
const screenSize = canvasHeight / window.devicePixelRatio;
|
|
540
543
|
const pixelSizeOnScreen = screenSize * factor;
|
|
541
544
|
for (let i = texture_lods_minmax.lods.length - 1; i >= 0; i--) {
|
package/lib/version.js
CHANGED
package/package.json
CHANGED