@needle-tools/gltf-progressive 2.1.6-next.c9b2e8b → 2.1.6-next.fa4044e
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/examples/react-three-fiber/src/App.tsx +38 -0
- package/examples/react-three-fiber/src/index.tsx +12 -0
- package/examples/react-three-fiber/src/styles.css +16 -0
- package/examples/react-three-fiber/tsconfig.json +22 -0
- package/gltf-progressive.js +1098 -0
- package/gltf-progressive.min.js +8 -0
- package/gltf-progressive.umd.cjs +8 -0
- package/{dist/lib → lib}/extension.d.ts +10 -6
- package/{dist/lib → lib}/extension.js +20 -94
- package/lib/lods.debug.d.ts +4 -0
- package/lib/lods.debug.js +41 -0
- package/{dist/lib → lib}/lods_manager.d.ts +6 -1
- package/{dist/lib → lib}/lods_manager.js +26 -10
- package/{dist/lib → lib}/plugins/plugin.d.ts +2 -2
- package/{dist/lib → lib}/version.js +1 -1
- package/package.json +14 -17
- package/.github/workflows/publish.yml +0 -47
- package/dist/CHANGELOG.md +0 -211
- package/dist/README.md +0 -129
- package/dist/examples/modelviewer-multiple.html +0 -126
- package/dist/examples/modelviewer.html +0 -34
- package/dist/examples/react-three-fiber/.prettierrc +0 -10
- package/dist/examples/react-three-fiber/favicon.png +0 -0
- package/dist/examples/react-three-fiber/index.html +0 -24
- package/dist/examples/react-three-fiber/package-lock.json +0 -4023
- package/dist/examples/react-three-fiber/package.json +0 -34
- package/dist/examples/react-three-fiber/vite.config.js +0 -39
- package/dist/examples/threejs/index.html +0 -52
- package/dist/examples/threejs/main.js +0 -181
- package/dist/gltf-progressive.js +0 -1107
- package/dist/gltf-progressive.min.js +0 -8
- package/dist/gltf-progressive.umd.cjs +0 -8
- package/dist/package.json +0 -65
- /package/{dist/lib → lib}/index.d.ts +0 -0
- /package/{dist/lib → lib}/index.js +0 -0
- /package/{dist/lib → lib}/loaders.d.ts +0 -0
- /package/{dist/lib → lib}/loaders.js +0 -0
- /package/{dist/lib → lib}/plugins/index.d.ts +0 -0
- /package/{dist/lib → lib}/plugins/index.js +0 -0
- /package/{dist/lib → lib}/plugins/modelviewer.d.ts +0 -0
- /package/{dist/lib → lib}/plugins/modelviewer.js +0 -0
- /package/{dist/lib → lib}/plugins/plugin.js +0 -0
- /package/{dist/lib → lib}/utils.d.ts +0 -0
- /package/{dist/lib → lib}/utils.internal.d.ts +0 -0
- /package/{dist/lib → lib}/utils.internal.js +0 -0
- /package/{dist/lib → lib}/utils.js +0 -0
- /package/{dist/lib → lib}/version.d.ts +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var He=Object.defineProperty,Ye=(t,e,s)=>e in t?He(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s,d=(t,e,s)=>(Ye(t,typeof e!="symbol"?e+"":e,s),s),Ee=(t,e,s)=>{if(!e.has(t))throw TypeError("Cannot "+s)},v=(t,e,s)=>(Ee(t,e,"read from private field"),s?s.call(t):e.get(t)),H=(t,e,s)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,s)},F=(t,e,s,n)=>(Ee(t,e,"write to private field"),n?n.call(t,s):e.set(t,s),s);import{BufferGeometry as ue,Mesh as Y,Texture as se,TextureLoader as Je,Matrix4 as Pe,Clock as Qe,MeshStandardMaterial as Ze,Sphere as et,Box3 as Ie,Vector3 as z}from"three";import{GLTFLoader as xe}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as tt}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as st}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as rt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ce="";globalThis.GLTF_PROGRESSIVE_VERSION=Ce,console.debug("[gltf-progressive] version -");let $="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",Q="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const ot=$,nt=Q,Be=new URL($+"draco_decoder.js");Be.searchParams.append("range","true"),fetch(Be,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${$} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),$===ot&&ke("./include/draco/"),Q===nt&&Re("./include/ktx2/")}).finally(()=>{je()});function ke(t){$=t,B&&B[ye]!=$?(console.debug("Updating Draco decoder path to "+t),B[ye]=$,B.setDecoderPath($),B.preload()):console.debug("Setting Draco decoder path to "+t)}function Re(t){Q=t,G&&G.transcoderPath!=Q?(console.debug("Updating KTX2 transcoder path to "+t),G.setTranscoderPath(Q),G.init()):console.debug("Setting KTX2 transcoder path to "+t)}const ye=Symbol("dracoDecoderPath");let B,ce,G;function je(){B||(B=new st,B[ye]=$,B.setDecoderPath($),B.setDecoderConfig({type:"js"}),B.preload()),G||(G=new rt,G.setTranscoderPath(Q),G.init()),ce||(ce=tt)}function Le(t){return je(),t?G.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:B,ktx2Loader:G,meshoptDecoder:ce}}function De(t){t.dracoLoader||t.setDRACOLoader(B),t.ktx2Loader||t.setKTX2Loader(G),t.meshoptDecoder||t.setMeshoptDecoder(ce)}const Me=new WeakMap;function we(t,e){let s=Me.get(t);s?s=Object.assign(s,e):s=e,Me.set(t,s)}const Ge=xe.prototype.load;function it(...t){const e=Me.get(this);let s=t[0];const n=new URL(s,window.location.href);if(n.hostname.endsWith("needle.tools")){const r=e?.progressive!==void 0?e.progressive:!0,o=e!=null&&e.usecase?e.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,s=n.toString()}return t[0]=s,Ge?.call(this,...t)}xe.prototype.load=it,re("debugprogressive");function re(t){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function at(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const s=t.lastIndexOf("/");if(s>=0){const n=t.substring(0,s+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}let de;function lt(){return de!==void 0||(de=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),re("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",de)),de}const ut=typeof window>"u"&&typeof document>"u",_e=Symbol("needle:raycast-mesh");function Z(t){return t?.[_e]instanceof ue?t[_e]:null}function Ne(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!Z(t)){const s=ct(e);s.userData={isRaycastMesh:!0},t[_e]=s}}function $e(t=!0){if(t){if(oe)return;const e=oe=Y.prototype.raycast;Y.prototype.raycast=function(s,n){const r=this,o=Z(r);let i;o&&r.isMesh&&(i=r.geometry,r.geometry=o),e.call(this,s,n),i&&(r.geometry=i)}}else{if(!oe)return;Y.prototype.raycast=oe,oe=null}}let oe=null;function ct(t){const e=new ue;for(const s in t.attributes)e.setAttribute(s,t.getAttribute(s));return e.setIndex(t.getIndex()),e}const V=new Array,L=re("debugprogressive");let he,J=-1;if(L){let t=function(){J+=1,J>=e&&(J=-1),console.log(`Toggle LOD level [${J}]`)},e=6;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(he=!he,console.log(`Toggle wireframe [${he}]`));const n=parseInt(s.key);!isNaN(n)&&n>=0&&(J=n,console.log(`Set LOD level to [${J}]`))})}function We(t){if(L)if(Array.isArray(t))for(const e of t)We(e);else t&&"wireframe"in t&&(t.wireframe=he===!0)}const W="NEEDLE_progressive",be=Symbol("needle-progressive-texture"),w=class{constructor(t,e){d(this,"parser"),d(this,"url"),d(this,"_isLoadingMesh"),d(this,"loadMesh",s=>{var n,r;if(this._isLoadingMesh)return null;const o=(r=(n=this.parser.json.meshes[s])==null?void 0:n.extensions)==null?void 0:r[W];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",s).then(i=>{var l;return this._isLoadingMesh=!1,i&&w.registerMesh(this.url,o.guid,i,(l=o.lods)==null?void 0:l.length,0,o),i})):null}),L&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return W}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var e;return((e=this.getAssignedLODInformation(t))==null?void 0:e.index)??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,n="LODS:minmax",r=t[n];if(r!=null)return r;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(L==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const l of Object.keys(i.uniforms)){const u=i.uniforms[l].value;u?.isTexture===!0&&o(u,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const l=t[i];l?.isTexture===!0&&o(l,e)}return t[n]=e,e;function o(i,l){const u=s.getAssignedLODInformation(i);if(u){const a=s.lodInfos.get(u.key);if(a&&a.lods){l.min_count=Math.min(l.min_count,a.lods.length),l.max_count=Math.max(l.max_count,a.lods.length);for(let h=0;h<a.lods.length;h++){const x=a.lods[h];x.width&&(l.lods[h]=l.lods[h]||{min_height:1/0,max_height:0},l.lods[h].min_height=Math.min(l.lods[h].min_height,x.height),l.lods[h].max_height=Math.max(l.lods[h].max_height,x.height))}}}}}static hasLODLevelAvailable(t,e){var s;if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let n,r;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(s=n?.userData)!=null&&s.LODS){const o=n.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){var s;if(!t)return Promise.resolve(null);if(t instanceof Y||t.isMesh===!0){const n=t.geometry,r=this.getAssignedLODInformation(n);if(!r)return Promise.resolve(null);for(const o of V)(s=o.onBeforeGetLODMesh)==null||s.call(o,t,e);return t["LOD:requested level"]=e,w.getOrLoadLOD(n,e).then(o=>{if(Array.isArray(o)){const i=r.index||0;o=o[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&n!=o&&(o?.isBufferGeometry?t.geometry=o:L&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),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.isMesh===!0){const s=t;if(Array.isArray(s.material)){const n=new Array;for(const r of s.material){const o=this.assignTextureLOD(r,e);n.push(o)}return Promise.all(n).then(r=>{const o=new Array;for(const i of r)Array.isArray(i)&&o.push(...i);return o})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,n=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const o=s;for(const i of Object.keys(o.uniforms)){const l=o.uniforms[i].value;if(l?.isTexture===!0){const u=this.assignTextureLODForSlot(l,e,s,i).then(a=>(a&&o.uniforms[i].value!=a&&(o.uniforms[i].value=a,o.uniformsNeedUpdate=!0),a));n.push(u),r.push(i)}}}else for(const o of Object.keys(s)){const i=s[o];if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,s,o);n.push(l),r.push(o)}}return Promise.all(n).then(o=>{const i=new Array;for(let l=0;l<o.length;l++){const u=o[l],a=r[l];u&&u.isTexture===!0?i.push({material:s,slot:a,texture:u,level:e}):i.push({material:s,slot:a,texture:null,level:e})}return i})}if(t instanceof se||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):w.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if(r?.isTexture===!0){if(r!=t&&s&&n){const o=s[n];if(o&&!L){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return L==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,s,o,r),null}s[n]=r}return r}else L=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}afterRoot(t){var e,s;return L&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,r)=>{var o;if(n!=null&&n.extensions){const i=n?.extensions[W];if(i){if(!i.lods){L&&console.warn("Texture has no LODs",i);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const a=this.parser.associations.get(u);a?.textures===r&&(l=!0,w.registerTexture(this.url,u,(o=i.lods)==null?void 0:o.length,r,i))}l||this.parser.getDependency("texture",r).then(u=>{var a;u&&w.registerTexture(this.url,u,(a=i.lods)==null?void 0:a.length,r,i)})}}}),(s=this.parser.json.meshes)==null||s.forEach((n,r)=>{if(n!=null&&n.extensions){const o=n?.extensions[W];if(o&&o.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const l=this.parser.associations.get(i);l?.meshes===r&&w.registerMesh(this.url,o.guid,i,o.lods.length,l.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var s,n,r,o;const i=L=="verbose",l=t.userData.LODS;if(!l)return null;const u=l?.key;let a;if(t.isTexture===!0){const h=t;h.source&&h.source[be]&&(a=h.source[be])}if(a||(a=w.lodInfos.get(u)),a){if(e>0){let m=!1;const M=Array.isArray(a.lods);if(M&&e>=a.lods.length?m=!0:M||(m=!0),m)return this.lowresCache.get(u)}const h=Array.isArray(a.lods)?(s=a.lods[e])==null?void 0:s.path:a.lods;if(!h)return L&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const x=at(l.url,h);if(x.endsWith(".glb")||x.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const m=x+"_"+a.guid,M=this.previouslyLoaded.get(m);if(M!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${m}`);let y=await M.catch(f=>(console.error(`Error loading LOD ${e} from ${x}
|
|
2
|
+
`,f),null)),p=!1;if(y==null||(y instanceof se&&t instanceof se?(n=y.image)!=null&&n.data||(r=y.source)!=null&&r.data?y=this.copySettings(t,y):(p=!0,this.previouslyLoaded.delete(m)):y instanceof ue&&t instanceof ue&&((o=y.attributes.position)!=null&&o.array||(p=!0,this.previouslyLoaded.delete(m)))),!p)return y}const b=a,R=new Promise(async(y,p)=>{const f=new xe;De(f),L&&(await new Promise(D=>setTimeout(D,1e3)),i&&console.warn("Start loading (delayed) "+x,b.guid));let U=x;if(b&&Array.isArray(b.lods)){const D=b.lods[e];D.hash&&(U+="?v="+D.hash)}const _=await f.loadAsync(U).catch(D=>(console.error(`Error loading LOD ${e} from ${x}
|
|
3
|
+
`,D),null));if(!_)return null;const j=_.parser;i&&console.log("Loading finished "+x,b.guid);let O=0;if(_.parser.json.textures){let D=!1;for(const c of _.parser.json.textures){if(c!=null&&c.extensions){const g=c?.extensions[W];if(g!=null&&g.guid&&g.guid===b.guid){D=!0;break}}O++}if(D){let c=await j.getDependency("texture",O);return c&&w.assignLODInformation(l.url,c,u,e,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+c.name+'"',x,O,c,m),t instanceof se&&(c=this.copySettings(t,c)),c&&(c.guid=b.guid),y(c)}else L&&console.warn("Could not find texture with guid",b.guid,_.parser.json)}if(O=0,_.parser.json.meshes){let D=!1;for(const c of _.parser.json.meshes){if(c!=null&&c.extensions){const g=c?.extensions[W];if(g!=null&&g.guid&&g.guid===b.guid){D=!0;break}}O++}if(D){const c=await j.getDependency("mesh",O);if(i&&console.log(`Loaded Mesh "${c.name}"`,x,O,c,m),c.isMesh===!0){const g=c.geometry;return w.assignLODInformation(l.url,g,u,e,0),y(g)}else{const g=new Array;for(let S=0;S<c.children.length;S++){const I=c.children[S];if(I.isMesh===!0){const C=I.geometry;w.assignLODInformation(l.url,C,u,e,S),g.push(C)}}return y(g)}}else L&&console.warn("Could not find mesh with guid",b.guid,_.parser.json)}return y(null)});return this.previouslyLoaded.set(m,R),await R}else if(t instanceof se){i&&console.log("Load texture from uri: "+x);const m=await new Je().loadAsync(x);return m?(m.guid=a.guid,m.flipY=!1,m.needsUpdate=!0,m.colorSpace=t.colorSpace,i&&console.log(a,m)):L&&console.warn("failed loading",x),m}}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,s,n,r){if(!e)return;e.userData||(e.userData={});const o=new dt(t,s,n,r);e.userData.LODS=o}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e?(L&&console.warn(`Copy texture settings
|
|
4
|
+
`,t.uuid,`
|
|
5
|
+
`,e.uuid),e=e.clone(),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):t}};let T=w;d(T,"registerTexture",(t,e,s,n,r)=>{if(L&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,r),!e){L&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[be]=r);const o=r.guid;w.assignLODInformation(t,e,o,s,n),w.lodInfos.set(o,r),w.lowresCache.set(o,e)}),d(T,"registerMesh",(t,e,s,n,r,o)=>{var i;const l=s.geometry;if(!l){L&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),L&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},o,s),w.assignLODInformation(t,l,e,n,r),w.lodInfos.set(e,o);let u=w.lowresCache.get(e);u?u.push(s.geometry):u=[s.geometry],w.lowresCache.set(e,u),n>0&&!Z(s)&&Ne(s,l);for(const a of V)(i=a.onRegisteredNewMesh)==null||i.call(a,s,o)}),d(T,"lodInfos",new Map),d(T,"previouslyLoaded",new Map),d(T,"lowresCache",new Map);class dt{constructor(e,s,n,r){d(this,"url"),d(this,"key"),d(this,"level"),d(this,"index"),this.url=e,this.key=s,this.level=n,r!=null&&(this.index=r)}}const N=re("debugprogressive"),ht=re("noprogressive"),Oe=Symbol("Needle:LODSManager"),Se=Symbol("Needle:LODState"),q=Symbol("Needle:CurrentLOD"),A={mesh_lod:-1,texture_lod:-1};var E,X,Te,ee,ne,ge,K;const P=class{constructor(t,e){d(this,"context"),d(this,"renderer"),d(this,"projectionScreenMatrix",new Pe),d(this,"targetTriangleDensity",2e5),d(this,"skinnedMeshAutoUpdateBoundsInterval",30),d(this,"updateInterval","auto"),H(this,E,1),d(this,"pause",!1),d(this,"manual",!1),d(this,"_lodchangedlisteners",[]),H(this,X,void 0),H(this,Te,new Qe),H(this,ee,0),H(this,ne,0),H(this,ge,0),H(this,K,0),d(this,"_fpsBuffer",[60,60,60,60,60]),d(this,"_sphere",new et),d(this,"_tempBox",new Ie),d(this,"_tempBox2",new Ie),d(this,"tempMatrix",new Pe),d(this,"_tempWorldPosition",new z),d(this,"_tempBoxSize",new z),d(this,"_tempBox2Size",new z),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Se]}static addPlugin(t){V.push(t)}static removePlugin(t){const e=V.indexOf(t);e>=0&&V.splice(e,1)}static get(t,e){if(t[Oe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Oe];const s=new P(t,{engine:"unknown",...e});return t[Oe]=s,s}get plugins(){return V}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(v(this,X))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;F(this,X,this.renderer.render);const e=this;Le(this.renderer),this.renderer.render=function(s,n){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,F(e,ee,v(e,ee)+1),F(e,ne,v(e,Te).getDelta()),F(e,ge,v(e,ge)+v(e,ne)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/v(e,ne)),F(e,K,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),N&&v(e,ee)%200===0&&console.log("FPS",Math.round(v(e,K)),"Interval:",v(e,E)));const o=t++;v(e,X).call(this,s,n),e.onAfterRender(s,n,o)}}disable(){v(this,X)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=v(this,X),F(this,X,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const n=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(n.length===1){const o=n[0].material;(o.name==="EffectMaterial"||o.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ht||(this.updateInterval==="auto"?v(this,K)<40&&v(this,E)<10?(F(this,E,v(this,E)+1),N&&console.warn("\u2193 Reducing LOD updates",v(this,E),v(this,K).toFixed(0))):v(this,K)>=60&&v(this,E)>1&&(F(this,E,v(this,E)-1),N&&console.warn("\u2191 Increasing LOD updates",v(this,E),v(this,K).toFixed(0))):F(this,E,this.updateInterval),v(this,E)>0&&v(this,ee)%v(this,E)!=0))return;this.internalUpdate(t,e)}}internalUpdate(t,e){var s,n;const r=this.renderer.renderLists.get(t,0),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const i=this.targetTriangleDensity;for(const a of o){if(a.material&&(((s=a.geometry)==null?void 0:s.type)==="BoxGeometry"||((n=a.geometry)==null?void 0:n.type)==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){N&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(N==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const x=Math.random()*16777215,m=new Ze({color:x});a.object.material=m}const h=a.object;(h instanceof Y||h.isMesh)&&this.updateLODs(t,e,h,i)}const l=r.transparent;for(const a of l){const h=a.object;(h instanceof Y||h.isMesh)&&this.updateLODs(t,e,h,i)}const u=r.transmissive;for(const a of u){const h=a.object;(h instanceof Y||h.isMesh)&&this.updateLODs(t,e,h,i)}}updateLODs(t,e,s,n){var r,o;s.userData||(s.userData={});let i=s[Se];if(i||(i=new gt,s[Se]=i),i.frames++<2)return;for(const u of V)(r=u.onBeforeUpdateLOD)==null||r.call(u,this.renderer,t,e,s);const l=P.overrideGlobalLodLevel!==void 0?P.overrideGlobalLodLevel:J;l>=0?(A.mesh_lod=l,A.texture_lod=l):(this.calculateLodLevel(e,s,i,n,A),A.mesh_lod=Math.round(A.mesh_lod),A.texture_lod=Math.round(A.texture_lod)),A.mesh_lod>=0&&this.loadProgressiveMeshes(s,A.mesh_lod),s.material&&A.texture_lod>=0&&this.loadProgressiveTextures(s.material,A.texture_lod),L&&s.material&&!s.isGizmo&&We(s.material);for(const u of V)(o=u.onAfterUpdatedLOD)==null||o.call(u,this.renderer,t,e,s,A);i.lastLodLevel_Mesh=A.mesh_lod,i.lastLodLevel_Texture=A.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let s=!1;(t[q]===void 0||e<t[q])&&(s=!0);const n=t["DEBUG:LOD"];n!=null&&(s=t[q]!=n,e=n),s&&(t[q]=e,T.assignTextureLOD(t,e).then(r=>{this._lodchangedlisteners.forEach(o=>o({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[q]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(s=t[q]!=n,e=n),s){t[q]=e;const r=t.geometry;return T.assignMeshLOD(t,e).then(o=>(o&&t[q]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(i=>i({type:"mesh",level:e,object:t})),o))}return Promise.resolve(null)}static isInside(t,e){const s=t.min,n=t.max,r=(s.x+n.x)*.5,o=(s.y+n.y)*.5;return this._tempPtInside.set(r,o,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,n,r){var o,i,l;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let u=10+1,a=!1;if(N&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const h=(o=T.getMeshLODExtension(e.geometry))==null?void 0:o.lods,x=T.getPrimitiveIndex(e.geometry),m=h&&h.length>0,M=T.getMaterialMinMaxLODsCount(e.material),b=M?.min_count!=1/0&&M.min_count>0&&M.max_count>0;if(!m&&!b){r.mesh_lod=0,r.texture_lod=0;return}m||(a=!0,u=0);const R=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let y=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const p=e;if(!p.boundingBox)p.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0&&s.frames%this.skinnedMeshAutoUpdateBoundsInterval===0){const f=Z(p),U=p.geometry;f&&(p.geometry=f),p.computeBoundingBox(),p.geometry=U}y=p.boundingBox}if(y){const p=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 c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(y),this._tempBox.applyMatrix4(e.matrixWorld),p.isPerspectiveCamera&&P.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&p.isPerspectiveCamera&&p.fov>70){const c=this._tempBox.min,g=this._tempBox.max;let S=c.x,I=c.y,C=g.x,te=g.y;const ie=2,pe=1.5,ae=(c.x+g.x)*.5,le=(c.y+g.y)*.5;S=(S-ae)*ie+ae,I=(I-le)*ie+le,C=(C-ae)*ie+ae,te=(te-le)*ie+le;const Xe=S<0&&C>0?0:Math.min(Math.abs(c.x),Math.abs(g.x)),Ke=I<0&&te>0?0:Math.min(Math.abs(c.y),Math.abs(g.y)),ve=Math.max(Xe,Ke);s.lastCentrality=(pe-ve)*(pe-ve)*(pe-ve)}else s.lastCentrality=1;const f=this._tempBox.getSize(this._tempBoxSize);f.multiplyScalar(.5),screen.availHeight>0&&R>0&&f.multiplyScalar(R/screen.availHeight),t.isPerspectiveCamera?f.x*=t.aspect:t.isOrthographicCamera;const U=t.matrixWorldInverse,_=this._tempBox2;_.copy(y),_.applyMatrix4(e.matrixWorld),_.applyMatrix4(U);const j=_.getSize(this._tempBox2Size),O=Math.max(j.x,j.y);if(Math.max(f.x,f.y)!=0&&O!=0&&(f.z=j.z/Math.max(j.x,j.y)*Math.max(f.x,f.y)),s.lastScreenCoverage=Math.max(f.x,f.y,f.z),s.lastScreenspaceVolume.copy(f),s.lastScreenCoverage*=s.lastCentrality,N&&P.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const g=P.corner0,S=P.corner1,I=P.corner2,C=P.corner3;g.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=g.x,I.copy(this._tempBox.max),I.y=g.y,C.copy(this._tempBox.max);const te=(g.z+C.z)*.5;g.z=S.z=I.z=C.z=te,g.applyMatrix4(c),S.applyMatrix4(c),I.applyMatrix4(c),C.applyMatrix4(c),P.debugDrawLine(g,S,255),P.debugDrawLine(g,I,255),P.debugDrawLine(S,C,255),P.debugDrawLine(I,C,255)}let D=999;if(h&&s.lastScreenCoverage>0)for(let c=0;c<h.length;c++){const g=h[c];if((((i=g.densities)==null?void 0:i[x])||g.density||1e-5)/s.lastScreenCoverage<n){D=c;break}}D<u&&(u=D,a=!0)}if(a?r.mesh_lod=u:r.mesh_lod=s.lastLodLevel_Mesh,N&&r.mesh_lod!=s.lastLodLevel_Mesh){const p=h?.[r.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (${p.density.toFixed(0)}) - ${e.name}`)}if(b){const p="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=M.max_count-1,N){const f=M.lods[M.max_count-1];N&&console.log(`First Texture LOD ${r.texture_lod} (${f.max_height}px) - ${e.name}`)}}else{const f=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let U=s.lastScreenCoverage*4;((l=this.context)==null?void 0:l.engine)==="model-viewer"&&(U*=1.5);const _=R/window.devicePixelRatio*U;let j=!1;for(let O=M.lods.length-1;O>=0;O--){const D=M.lods[O];if(!(p&&D.max_height>=2048)&&!(lt()&&D.max_height>4096)&&(D.max_height>_||!j&&O===0)){if(j=!0,r.texture_lod=O,r.texture_lod<s.lastLodLevel_Texture){const c=D.max_height;N&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${c}px
|
|
6
|
+
Screensize: ${_.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${f.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else r.texture_lod=0}};let k=P;E=new WeakMap,X=new WeakMap,Te=new WeakMap,ee=new WeakMap,ne=new WeakMap,ge=new WeakMap,K=new WeakMap,d(k,"debugDrawLine"),d(k,"overrideGlobalLodLevel"),d(k,"corner0",new z),d(k,"corner1",new z),d(k,"corner2",new z),d(k,"corner3",new z),d(k,"_tempPtInside",new z);class gt{constructor(){d(this,"frames",0),d(this,"lastLodLevel_Mesh",-1),d(this,"lastLodLevel_Texture",-1),d(this,"lastScreenCoverage",0),d(this,"lastScreenspaceVolume",new z),d(this,"lastCentrality",0)}}const Ue=Symbol("NEEDLE_mesh_lod"),fe=Symbol("NEEDLE_texture_lod");let me=null;function Ae(){const t=ft();t&&(t.mapURLs(function(e){return Fe(),e}),Fe(),me?.disconnect(),me=new MutationObserver(e=>{e.forEach(s=>{s.addedNodes.forEach(n=>{n instanceof HTMLElement&&n.tagName.toLowerCase()==="model-viewer"&&Ve(n)})})}),me.observe(document,{childList:!0,subtree:!0}))}function ft(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ae()}),null)}function Fe(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(t=>{Ve(t)})}const ze=new WeakSet;let mt=0;function Ve(t){if(!t||ze.has(t))return null;ze.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++mt+`
|
|
8
|
+
`,t.getAttribute("src"));let e=null,s=null,n=null;for(let r=t;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),i=o.find(a=>a.toString()=="Symbol(renderer)"),l=o.find(a=>a.toString()=="Symbol(scene)"),u=o.find(a=>a.toString()=="Symbol(needsRender)");!e&&i!=null&&(e=t[i].threeRenderer),!s&&l!=null&&(s=t[l]),!n&&u!=null&&(n=t[u])}if(e&&s){let r=function(){if(n){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}n?.call(t)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=k.get(e,{engine:"model-viewer"});return k.addPlugin(new pt),o.enable(),o.addEventListener("changed",()=>{n?.call(t)}),t.addEventListener("model-visibility",i=>{i.detail.visible&&n?.call(t)}),t.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class pt{constructor(){d(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,s,n,r){this.tryParseMeshLOD(s,r),this.tryParseTextureLOD(s,r)}getUrl(e){if(!e)return null;let s=e.getAttribute("src");return s||(s=e.src),s||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),s}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,s){if(s[fe]==!0)return;s[fe]=!0;const n=this.tryGetCurrentGLTF(e),r=this.tryGetCurrentModelViewer(e),o=this.getUrl(r);if(o&&n&&s.material){let i=function(u){var a,h,x;if(u[fe]==!0)return;u[fe]=!0,u.userData&&(u.userData.LOD=-1);const m=Object.keys(u);for(let M=0;M<m.length;M++){const b=m[M],R=u[b];if(R?.isTexture===!0){const y=(h=(a=R.userData)==null?void 0:a.associations)==null?void 0:h.textures;if(y==null)continue;const p=n.parser.json.textures[y];if(!p){console.warn("Texture data not found for texture index "+y);continue}if((x=p?.extensions)!=null&&x[W]){const f=p.extensions[W];f&&o&&T.registerTexture(o,R,f.lods.length,y,f)}}}};const l=s.material;if(Array.isArray(l))for(const u of l)i(u);else i(l)}}tryParseMeshLOD(e,s){var n,r;if(s[Ue]==!0)return;s[Ue]=!0;const o=this.tryGetCurrentModelViewer(e),i=this.getUrl(o);if(!i)return;const l=(r=(n=s.userData)==null?void 0:n.gltfExtensions)==null?void 0:r[W];if(l&&i){const u=s.uuid;T.registerMesh(i,u,s,0,l.lods.length,l)}}}function qe(t,e,s,n){Le(e),De(s),we(s,{progressive:!0,...n?.hints}),s.register(o=>new T(o,t));const r=k.get(e);return n?.enableLODsManager!==!1&&r.enable(),r}if(Ae(),!ut){const t={gltfProgressive:{useNeedleProgressive:qe,LODsManager:k,configureLoader:we,getRaycastMesh:Z,useRaycastMeshes:$e}};if(!globalThis.Needle)globalThis.Needle=t;else for(const e in t)globalThis.Needle[e]=t[e]}export{W as EXTENSION_NAME,k as LODsManager,T as NEEDLE_progressive,Ce as VERSION,De as addDracoAndKTX2Loaders,we as configureLoader,Le as createLoaders,Z as getRaycastMesh,Ae as patchModelViewer,Ne as registerRaycastMesh,ke as setDracoDecoderLocation,Re as setKTX2TranscoderLocation,qe as useNeedleProgressive,$e as useRaycastMeshes};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";var Ye=Object.defineProperty;var He=(n,t,e)=>t in n?Ye(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var d=(n,t,e)=>(He(n,typeof t!="symbol"?t+"":t,e),e),Ce=(n,t,e)=>{if(!t.has(n))throw TypeError("Cannot "+e)};var L=(n,t,e)=>(Ce(n,t,"read from private field"),e?e.call(n):t.get(n)),J=(n,t,e)=>{if(t.has(n))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(n):t.set(n,e)},W=(n,t,e,r)=>(Ce(n,t,"write to private field"),r?r.call(n,e):t.set(n,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("three"),Se=require("three/examples/jsm/loaders/GLTFLoader.js"),je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Je=require("three/examples/jsm/loaders/DRACOLoader.js"),Qe=require("three/examples/jsm/loaders/KTX2Loader.js"),Ge="";globalThis.GLTF_PROGRESSIVE_VERSION=Ge;console.debug("[gltf-progressive] version -");let X="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",te="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ze=X,et=te,ke=new URL(X+"draco_decoder.js");ke.searchParams.append("range","true");fetch(ke,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${X} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),X===Ze&&$e("./include/draco/"),te===et&&Fe("./include/ktx2/")}).finally(()=>{Ne()});function $e(n){X=n,G&&G[_e]!=X?(console.debug("Updating Draco decoder path to "+n),G[_e]=X,G.setDecoderPath(X),G.preload()):console.debug("Setting Draco decoder path to "+n)}function Fe(n){te=n,U&&U.transcoderPath!=te?(console.debug("Updating KTX2 transcoder path to "+n),U.setTranscoderPath(te),U.init()):console.debug("Setting KTX2 transcoder path to "+n)}const _e=Symbol("dracoDecoderPath");let G,pe,U;function Ne(){G||(G=new Je.DRACOLoader,G[_e]=X,G.setDecoderPath(X),G.setDecoderConfig({type:"js"}),G.preload()),U||(U=new Qe.KTX2Loader,U.setTranscoderPath(te),U.init()),pe||(pe=je.MeshoptDecoder)}function Te(n){return Ne(),n?U.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:G,ktx2Loader:U,meshoptDecoder:pe}}function Ae(n){n.dracoLoader||n.setDRACOLoader(G),n.ktx2Loader||n.setKTX2Loader(U),n.meshoptDecoder||n.setMeshoptDecoder(pe)}const Oe=new WeakMap;function Pe(n,t){let e=Oe.get(n);e?e=Object.assign(e,t):e=t,Oe.set(n,e)}const Me=Se.GLTFLoader.prototype.load;function tt(...n){const t=Oe.get(this);let e=n[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=(t==null?void 0:t.progressive)!==void 0?t.progressive:!0,s=t!=null&&t.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return n[0]=e,Me==null?void 0:Me.call(this,...n)}Se.GLTFLoader.prototype.load=tt;ce("debugprogressive");function ce(n){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function rt(n,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||n===void 0)return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let ne;function st(){return ne!==void 0||(ne=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),ce("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ne)),ne}const ot=typeof window>"u"&&typeof document>"u",be=Symbol("needle:raycast-mesh");function oe(n){return(n==null?void 0:n[be])instanceof p.BufferGeometry?n[be]:null}function Ue(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!oe(n)){const r=it(t);r.userData={isRaycastMesh:!0},n[be]=r}}function Ve(n=!0){if(n){if(ae)return;const t=ae=p.Mesh.prototype.raycast;p.Mesh.prototype.raycast=function(e,r){const i=this,o=oe(i);let s;o&&i.isMesh&&(s=i.geometry,i.geometry=o),t.call(this,e,r),s&&(i.geometry=s)}}else{if(!ae)return;p.Mesh.prototype.raycast=ae,ae=null}}let ae=null;function it(n){const t=new p.BufferGeometry;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const Z=new Array,x=ce("debugprogressive");let ge,ee=-1;if(x){let n=function(){ee+=1,ee>=t&&(ee=-1),console.log(`Toggle LOD level [${ee}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(ge=!ge,console.log(`Toggle wireframe [${ge}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(ee=r,console.log(`Set LOD level to [${ee}]`))})}function ze(n){if(x)if(Array.isArray(n))for(const t of n)ze(t);else n&&"wireframe"in n&&(n.wireframe=ge===!0)}const q="NEEDLE_progressive",De=Symbol("needle-progressive-texture"),S=class{constructor(t,e){d(this,"parser");d(this,"url");d(this,"_isLoadingMesh");d(this,"loadMesh",t=>{var r,i;if(this._isLoadingMesh)return null;const e=(i=(r=this.parser.json.meshes[t])==null?void 0:r.extensions)==null?void 0:i[q];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(o=>{var s;return this._isLoadingMesh=!1,o&&S.registerMesh(this.url,e.guid,o,(s=e.lods)==null?void 0:s.length,0,e),o})):null});x&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return q}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var r;const e=(r=this.getAssignedLODInformation(t))==null?void 0:r.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const r=this,i="LODS:minmax",o=t[i];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[i]=e,e}if(x==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;(u==null?void 0:u.isTexture)===!0&&s(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];(l==null?void 0:l.isTexture)===!0&&s(l,e)}return t[i]=e,e;function s(a,l){const u=r.getAssignedLODInformation(a);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let h=0;h<c.lods.length;h++){const g=c.lods[h];g.width&&(l.lods[h]=l.lods[h]||{min_height:1/0,max_height:0},l.lods[h].min_height=Math.min(l.lods[h].min_height,g.height),l.lods[h].max_height=Math.max(l.lods[h].max_height,g.height))}}}}}static hasLODLevelAvailable(t,e){var o;if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const a=t[s];if(a&&a.isTexture&&this.hasLODLevelAvailable(a,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,i;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&(o=r==null?void 0:r.userData)!=null&&o.LODS){const s=r.userData.LODS;if(i=this.lodInfos.get(s.key),e===void 0)return i!=null;if(i)return Array.isArray(i.lods)?e<i.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof p.Mesh||t.isMesh===!0){const i=t.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of Z)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,S.getOrLoadLOD(i,e).then(s=>{if(Array.isArray(s)){const a=o.index||0;s=s[a]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&i!=s&&((s==null?void 0:s.isBufferGeometry)?t.geometry=s:x&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else x&&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.isMesh===!0){const r=t;if(Array.isArray(r.material)){const i=new Array;for(const o of r.material){const s=this.assignTextureLOD(o,e);i.push(s)}return Promise.all(i).then(o=>{const s=new Array;for(const a of o)Array.isArray(a)&&s.push(...a);return s})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,i=[],o=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const s=r;for(const a of Object.keys(s.uniforms)){const l=s.uniforms[a].value;if((l==null?void 0:l.isTexture)===!0){const u=this.assignTextureLODForSlot(l,e,r,a).then(c=>(c&&s.uniforms[a].value!=c&&(s.uniforms[a].value=c,s.uniformsNeedUpdate=!0),c));i.push(u),o.push(a)}}}else for(const s of Object.keys(r)){const a=r[s];if((a==null?void 0:a.isTexture)===!0){const l=this.assignTextureLODForSlot(a,e,r,s);i.push(l),o.push(s)}}return Promise.all(i).then(s=>{const a=new Array;for(let l=0;l<s.length;l++){const u=s[l],c=o[l];u&&u.isTexture===!0?a.push({material:r,slot:c,texture:u,level:e}):a.push({material:r,slot:c,texture:null,level:e})}return a})}if(t instanceof p.Texture||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,i){return(t==null?void 0:t.isTexture)!==!0?Promise.resolve(null):i==="glyphMap"?Promise.resolve(t):S.getOrLoadLOD(t,e).then(o=>{if(Array.isArray(o))return null;if((o==null?void 0:o.isTexture)===!0){if(o!=t&&r&&i){const s=r[i];if(s&&!x){const a=this.getAssignedLODInformation(s);if(a&&(a==null?void 0:a.level)<e)return x==="verbose"&&console.warn("Assigned texture level is already higher: ",a.level,e,r,s,o),null}r[i]=o}return o}else x=="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 x&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((i,o)=>{var s;if(i!=null&&i.extensions){const a=i==null?void 0:i.extensions[q];if(a){if(!a.lods){x&&console.warn("Texture has no LODs",a);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const c=this.parser.associations.get(u);(c==null?void 0:c.textures)===o&&(l=!0,S.registerTexture(this.url,u,(s=a.lods)==null?void 0:s.length,o,a))}l||this.parser.getDependency("texture",o).then(u=>{var c;u&&S.registerTexture(this.url,u,(c=a.lods)==null?void 0:c.length,o,a)})}}}),(r=this.parser.json.meshes)==null||r.forEach((i,o)=>{if(i!=null&&i.extensions){const s=i==null?void 0:i.extensions[q];if(s&&s.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const l=this.parser.associations.get(a);(l==null?void 0:l.meshes)===o&&S.registerMesh(this.url,s.guid,a,s.lods.length,l.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var a,l,u,c;const r=x=="verbose",i=t.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(t.isTexture===!0){const h=t;h.source&&h.source[De]&&(s=h.source[De])}if(s||(s=S.lodInfos.get(o)),s){if(e>0){let D=!1;const O=Array.isArray(s.lods);if(O&&e>=s.lods.length?D=!0:O||(D=!0),D)return this.lowresCache.get(o)}const h=Array.isArray(s.lods)?(a=s.lods[e])==null?void 0:a.path:s.lods;if(!h)return x&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,s)),null;const g=rt(i.url,h);if(g.endsWith(".glb")||g.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const D=g+"_"+s.guid,O=this.previouslyLoaded.get(D);if(O!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${D}`);let v=await O.catch(y=>(console.error(`Error loading LOD ${e} from ${g}
|
|
2
|
+
`,y),null)),_=!1;if(v==null||(v instanceof p.Texture&&t instanceof p.Texture?(l=v.image)!=null&&l.data||(u=v.source)!=null&&u.data?v=this.copySettings(t,v):(_=!0,this.previouslyLoaded.delete(D)):v instanceof p.BufferGeometry&&t instanceof p.BufferGeometry&&((c=v.attributes.position)!=null&&c.array||(_=!0,this.previouslyLoaded.delete(D)))),!_)return v}const M=s,V=new Promise(async(v,_)=>{const y=new Se.GLTFLoader;Ae(y),x&&(await new Promise(b=>setTimeout(b,1e3)),r&&console.warn("Start loading (delayed) "+g,M.guid));let Y=g;if(M&&Array.isArray(M.lods)){const b=M.lods[e];b.hash&&(Y+="?v="+b.hash)}const E=await y.loadAsync(Y).catch(b=>(console.error(`Error loading LOD ${e} from ${g}
|
|
3
|
+
`,b),null));if(!E)return null;const $=E.parser;r&&console.log("Loading finished "+g,M.guid);let k=0;if(E.parser.json.textures){let b=!1;for(const f of E.parser.json.textures){if(f!=null&&f.extensions){const T=f==null?void 0:f.extensions[q];if(T!=null&&T.guid&&T.guid===M.guid){b=!0;break}}k++}if(b){let f=await $.getDependency("texture",k);return f&&S.assignLODInformation(i.url,f,o,e,void 0),r&&console.log('change "'+t.name+'" → "'+f.name+'"',g,k,f,D),t instanceof p.Texture&&(f=this.copySettings(t,f)),f&&(f.guid=M.guid),v(f)}else x&&console.warn("Could not find texture with guid",M.guid,E.parser.json)}if(k=0,E.parser.json.meshes){let b=!1;for(const f of E.parser.json.meshes){if(f!=null&&f.extensions){const T=f==null?void 0:f.extensions[q];if(T!=null&&T.guid&&T.guid===M.guid){b=!0;break}}k++}if(b){const f=await $.getDependency("mesh",k);if(r&&console.log(`Loaded Mesh "${f.name}"`,g,k,f,D),f.isMesh===!0){const T=f.geometry;return S.assignLODInformation(i.url,T,o,e,0),v(T)}else{const T=new Array;for(let m=0;m<f.children.length;m++){const w=f.children[m];if(w.isMesh===!0){const C=w.geometry;S.assignLODInformation(i.url,C,o,e,m),T.push(C)}}return v(T)}}else x&&console.warn("Could not find mesh with guid",M.guid,E.parser.json)}return v(null)});return this.previouslyLoaded.set(D,V),await V}else if(t instanceof p.Texture){r&&console.log("Load texture from uri: "+g);const O=await new p.TextureLoader().loadAsync(g);return O?(O.guid=s.guid,O.flipY=!1,O.needsUpdate=!0,O.colorSpace=t.colorSpace,r&&console.log(s,O)):x&&console.warn("failed loading",g),O}}else x&&console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,i,o){if(!e)return;e.userData||(e.userData={});const s=new nt(t,r,i,o);e.userData.LODS=s}static getAssignedLODInformation(t){var e;return((e=t==null?void 0:t.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e?(x&&console.warn(`Copy texture settings
|
|
4
|
+
`,t.uuid,`
|
|
5
|
+
`,e.uuid),e=e.clone(),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):t}};let A=S;d(A,"registerTexture",(t,e,r,i,o)=>{if(x&&console.log("> Progressive: register texture",i,e.name,e.uuid,e,o),!e){x&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[De]=o);const s=o.guid;S.assignLODInformation(t,e,s,r,i),S.lodInfos.set(s,o),S.lowresCache.set(s,e)}),d(A,"registerMesh",(t,e,r,i,o,s)=>{var u;const a=r.geometry;if(!a){x&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),x&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},s,r),S.assignLODInformation(t,a,e,i,o),S.lodInfos.set(e,s);let l=S.lowresCache.get(e);l?l.push(r.geometry):l=[r.geometry],S.lowresCache.set(e,l),i>0&&!oe(r)&&Ue(r,a);for(const c of Z)(u=c.onRegisteredNewMesh)==null||u.call(c,r,s)}),d(A,"lodInfos",new Map),d(A,"previouslyLoaded",new Map),d(A,"lowresCache",new Map);class nt{constructor(t,e,r,i){d(this,"url");d(this,"key");d(this,"level");d(this,"index");this.url=t,this.key=e,this.level=r,i!=null&&(this.index=i)}}const N=ce("debugprogressive"),at=ce("noprogressive"),ve=Symbol("Needle:LODSManager"),we=Symbol("Needle:LODState"),Q=Symbol("Needle:CurrentLOD"),B={mesh_lod:-1,texture_lod:-1};var I,H,ye,re,se,Le,j;const P=class{constructor(t,e){d(this,"context");d(this,"renderer");d(this,"projectionScreenMatrix",new p.Matrix4);d(this,"targetTriangleDensity",2e5);d(this,"skinnedMeshAutoUpdateBoundsInterval",30);d(this,"updateInterval","auto");J(this,I,1);d(this,"pause",!1);d(this,"manual",!1);d(this,"_lodchangedlisteners",[]);J(this,H,void 0);J(this,ye,new p.Clock);J(this,re,0);J(this,se,0);J(this,Le,0);J(this,j,0);d(this,"_fpsBuffer",[60,60,60,60,60]);d(this,"_sphere",new p.Sphere);d(this,"_tempBox",new p.Box3);d(this,"_tempBox2",new p.Box3);d(this,"tempMatrix",new p.Matrix4);d(this,"_tempWorldPosition",new p.Vector3);d(this,"_tempBoxSize",new p.Vector3);d(this,"_tempBox2Size",new p.Vector3);this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[we]}static addPlugin(t){Z.push(t)}static removePlugin(t){const e=Z.indexOf(t);e>=0&&Z.splice(e,1)}static get(t,e){if(t[ve])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ve];const r=new P(t,{engine:"unknown",...e});return t[ve]=r,r}get plugins(){return Z}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}enable(){if(L(this,H))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;W(this,H,this.renderer.render);const e=this;Te(this.renderer),this.renderer.render=function(r,i){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,W(e,re,L(e,re)+1),W(e,se,L(e,ye).getDelta()),W(e,Le,L(e,Le)+L(e,se)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/L(e,se)),W(e,j,e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length),N&&L(e,re)%200===0&&console.log("FPS",Math.round(L(e,j)),"Interval:",L(e,I)));const s=t++;L(e,H).call(this,r,i),e.onAfterRender(r,i,s)}}disable(){L(this,H)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=L(this,H),W(this,H,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const a=o[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(at||(this.updateInterval==="auto"?L(this,j)<40&&L(this,I)<10?(W(this,I,L(this,I)+1),N&&console.warn("↓ Reducing LOD updates",L(this,I),L(this,j).toFixed(0))):L(this,j)>=60&&L(this,I)>1&&(W(this,I,L(this,I)-1),N&&console.warn("↑ Increasing LOD updates",L(this,I),L(this,j).toFixed(0))):W(this,I,this.updateInterval),L(this,I)>0&&L(this,re)%L(this,I)!=0))return;this.internalUpdate(t,e)}}internalUpdate(t,e){var l,u;const r=this.renderer.renderLists.get(t,0),i=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const c of i){if(c.material&&(((l=c.geometry)==null?void 0:l.type)==="BoxGeometry"||((u=c.geometry)==null?void 0:u.type)==="BufferGeometry")&&(c.material.name==="SphericalGaussianBlur"||c.material.name=="BackgroundCubeMaterial"||c.material.name==="CubemapFromEquirect"||c.material.name==="EquirectangularToCubeUV")){N&&(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",c,c.material.name,c.material.type)));continue}switch(c.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(N==="color"&&c.material&&!c.object.progressive_debug_color){c.object.progressive_debug_color=!0;const g=Math.random()*16777215,D=new p.MeshStandardMaterial({color:g});c.object.material=D}const h=c.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,o)}const s=r.transparent;for(const c of s){const h=c.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,o)}const a=r.transmissive;for(const c of a){const h=c.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,o)}}updateLODs(t,e,r,i){var a,l;r.userData||(r.userData={});let o=r[we];if(o||(o=new lt,r[we]=o),o.frames++<2)return;for(const u of Z)(a=u.onBeforeUpdateLOD)==null||a.call(u,this.renderer,t,e,r);const s=P.overrideGlobalLodLevel!==void 0?P.overrideGlobalLodLevel:ee;s>=0?(B.mesh_lod=s,B.texture_lod=s):(this.calculateLodLevel(e,r,o,i,B),B.mesh_lod=Math.round(B.mesh_lod),B.texture_lod=Math.round(B.texture_lod)),B.mesh_lod>=0&&this.loadProgressiveMeshes(r,B.mesh_lod),r.material&&B.texture_lod>=0&&this.loadProgressiveTextures(r.material,B.texture_lod),x&&r.material&&!r.isGizmo&&ze(r.material);for(const u of Z)(l=u.onAfterUpdatedLOD)==null||l.call(u,this.renderer,t,e,r,B);o.lastLodLevel_Mesh=B.mesh_lod,o.lastLodLevel_Texture=B.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let r=!1;(t[Q]===void 0||e<t[Q])&&(r=!0);const i=t["DEBUG:LOD"];i!=null&&(r=t[Q]!=i,e=i),r&&(t[Q]=e,A.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(s=>s({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[Q]!==e;const i=t["DEBUG:LOD"];if(i!=null&&(r=t[Q]!=i,e=i),r){t[Q]=e;const o=t.geometry;return A.assignMeshLOD(t,e).then(s=>(s&&t[Q]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),s))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,i=t.max,o=(r.x+i.x)*.5,s=(r.y+i.y)*.5;return this._tempPtInside.set(o,s,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,i,o){var V,K,v;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(N&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=(V=A.getMeshLODExtension(e.geometry))==null?void 0:V.lods,c=A.getPrimitiveIndex(e.geometry),h=u&&u.length>0,g=A.getMaterialMinMaxLODsCount(e.material),D=(g==null?void 0:g.min_count)!=1/0&&g.min_count>0&&g.max_count>0;if(!h&&!D){o.mesh_lod=0,o.texture_lod=0;return}h||(l=!0,a=0);const O=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const _=e;if(!_.boundingBox)_.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0&&r.frames%this.skinnedMeshAutoUpdateBoundsInterval===0){const y=oe(_),Y=_.geometry;y&&(_.geometry=y),_.computeBoundingBox(),_.geometry=Y}M=_.boundingBox}if(M){const _=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 m=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(m)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(M),this._tempBox.applyMatrix4(e.matrixWorld),_.isPerspectiveCamera&&P.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&_.isPerspectiveCamera&&_.fov>70){const m=this._tempBox.min,w=this._tempBox.max;let C=m.x,F=m.y,z=w.x,ie=w.y;const ue=2,me=1.5,de=(m.x+w.x)*.5,fe=(m.y+w.y)*.5;C=(C-de)*ue+de,F=(F-fe)*ue+fe,z=(z-de)*ue+de,ie=(ie-fe)*ue+fe;const Xe=C<0&&z>0?0:Math.min(Math.abs(m.x),Math.abs(w.x)),Ke=F<0&&ie>0?0:Math.min(Math.abs(m.y),Math.abs(w.y)),xe=Math.max(Xe,Ke);r.lastCentrality=(me-xe)*(me-xe)*(me-xe)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&O>0&&y.multiplyScalar(O/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const Y=t.matrixWorldInverse,E=this._tempBox2;E.copy(M),E.applyMatrix4(e.matrixWorld),E.applyMatrix4(Y);const $=E.getSize(this._tempBox2Size),k=Math.max($.x,$.y);if(Math.max(y.x,y.y)!=0&&k!=0&&(y.z=$.z/Math.max($.x,$.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,N&&P.debugDrawLine){const m=this.tempMatrix.copy(this.projectionScreenMatrix);m.invert();const w=P.corner0,C=P.corner1,F=P.corner2,z=P.corner3;w.copy(this._tempBox.min),C.copy(this._tempBox.max),C.x=w.x,F.copy(this._tempBox.max),F.y=w.y,z.copy(this._tempBox.max);const ie=(w.z+z.z)*.5;w.z=C.z=F.z=z.z=ie,w.applyMatrix4(m),C.applyMatrix4(m),F.applyMatrix4(m),z.applyMatrix4(m),P.debugDrawLine(w,C,255),P.debugDrawLine(w,F,255),P.debugDrawLine(C,z,255),P.debugDrawLine(F,z,255)}let f=999;if(u&&r.lastScreenCoverage>0)for(let m=0;m<u.length;m++){const w=u[m];if((((K=w.densities)==null?void 0:K[c])||w.density||1e-5)/r.lastScreenCoverage<i){f=m;break}}f<a&&(a=f,l=!0)}if(l?o.mesh_lod=a:o.mesh_lod=r.lastLodLevel_Mesh,N&&o.mesh_lod!=r.lastLodLevel_Mesh){const y=u==null?void 0:u[o.mesh_lod];y&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${o.mesh_lod} (${y.density.toFixed(0)}) - ${e.name}`)}if(D){const _="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=g.max_count-1,N){const y=g.lods[g.max_count-1];N&&console.log(`First Texture LOD ${o.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let Y=r.lastScreenCoverage*4;((v=this.context)==null?void 0:v.engine)==="model-viewer"&&(Y*=1.5);const $=O/window.devicePixelRatio*Y;let k=!1;for(let b=g.lods.length-1;b>=0;b--){const f=g.lods[b];if(!(_&&f.max_height>=2048)&&!(st()&&f.max_height>4096)&&(f.max_height>$||!k&&b===0)){if(k=!0,o.texture_lod=b,o.texture_lod<r.lastLodLevel_Texture){const T=f.max_height;N&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${o.texture_lod} = ${T}px
|
|
6
|
+
Screensize: ${$.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else o.texture_lod=0}};let R=P;I=new WeakMap,H=new WeakMap,ye=new WeakMap,re=new WeakMap,se=new WeakMap,Le=new WeakMap,j=new WeakMap,d(R,"debugDrawLine"),d(R,"overrideGlobalLodLevel"),d(R,"corner0",new p.Vector3),d(R,"corner1",new p.Vector3),d(R,"corner2",new p.Vector3),d(R,"corner3",new p.Vector3),d(R,"_tempPtInside",new p.Vector3);class lt{constructor(){d(this,"frames",0);d(this,"lastLodLevel_Mesh",-1);d(this,"lastLodLevel_Texture",-1);d(this,"lastScreenCoverage",0);d(this,"lastScreenspaceVolume",new p.Vector3);d(this,"lastCentrality",0)}}const Be=Symbol("NEEDLE_mesh_lod"),he=Symbol("NEEDLE_texture_lod");let le=null;function Ee(){const n=ct();n&&(n.mapURLs(function(t){return Re(),t}),Re(),le==null||le.disconnect(),le=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&We(r)})})}),le.observe(document,{childList:!0,subtree:!0}))}function ct(){if(typeof customElements>"u")return null;const n=customElements.get("model-viewer");return n||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ee()}),null)}function Re(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{We(t)})}const Ie=new WeakSet;let ut=0;function We(n){if(!n||Ie.has(n))return null;Ie.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++ut+`
|
|
8
|
+
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let i=n;i!=null;i=Object.getPrototypeOf(i)){const o=Object.getOwnPropertySymbols(i),s=o.find(u=>u.toString()=="Symbol(renderer)"),a=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&s!=null&&(t=n[s].threeRenderer),!e&&a!=null&&(e=n[a]),!r&&l!=null&&(r=n[l])}if(t&&e){let i=function(){if(r){let s=0,a=setInterval(()=>{if(s++>5){clearInterval(a);return}r==null||r.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=R.get(t,{engine:"model-viewer"});return R.addPlugin(new dt),o.enable(),o.addEventListener("changed",()=>{r==null||r.call(n)}),n.addEventListener("model-visibility",s=>{s.detail.visible&&(r==null||r.call(n))}),n.addEventListener("load",()=>{i()}),()=>{o.disable()}}return null}class dt{constructor(){d(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(t,e,r,i){this.tryParseMeshLOD(e,i),this.tryParseTextureLOD(e,i)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[he]==!0)return;e[he]=!0;const r=this.tryGetCurrentGLTF(t),i=this.tryGetCurrentModelViewer(t),o=this.getUrl(i);if(o&&r&&e.material){let s=function(l){var c,h,g;if(l[he]==!0)return;l[he]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let D=0;D<u.length;D++){const O=u[D],M=l[O];if((M==null?void 0:M.isTexture)===!0){const V=(h=(c=M.userData)==null?void 0:c.associations)==null?void 0:h.textures;if(V==null)continue;const K=r.parser.json.textures[V];if(!K){console.warn("Texture data not found for texture index "+V);continue}if((g=K==null?void 0:K.extensions)!=null&&g[q]){const v=K.extensions[q];v&&o&&A.registerTexture(o,M,v.lods.length,V,v)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)s(l);else s(a)}}tryParseMeshLOD(t,e){var s,a;if(e[Be]==!0)return;e[Be]=!0;const r=this.tryGetCurrentModelViewer(t),i=this.getUrl(r);if(!i)return;const o=(a=(s=e.userData)==null?void 0:s.gltfExtensions)==null?void 0:a[q];if(o&&i){const l=e.uuid;A.registerMesh(i,l,e,0,o.lods.length,o)}}}function qe(n,t,e,r){Te(t),Ae(e),Pe(e,{progressive:!0,...r==null?void 0:r.hints}),e.register(o=>new A(o,n));const i=R.get(t);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}Ee();if(!ot){const n={gltfProgressive:{useNeedleProgressive:qe,LODsManager:R,configureLoader:Pe,getRaycastMesh:oe,useRaycastMeshes:Ve}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}exports.EXTENSION_NAME=q;exports.LODsManager=R;exports.NEEDLE_progressive=A;exports.VERSION=Ge;exports.addDracoAndKTX2Loaders=Ae;exports.configureLoader=Pe;exports.createLoaders=Te;exports.getRaycastMesh=oe;exports.patchModelViewer=Ee;exports.registerRaycastMesh=Ue;exports.setDracoDecoderLocation=$e;exports.setKTX2TranscoderLocation=Fe;exports.useNeedleProgressive=qe;exports.useRaycastMeshes=Ve;
|
|
@@ -6,22 +6,25 @@ declare type NEEDLE_progressive_model_LOD = {
|
|
|
6
6
|
hash?: string;
|
|
7
7
|
};
|
|
8
8
|
/** This is the data structure we have in the NEEDLE_progressive extension */
|
|
9
|
-
declare type
|
|
9
|
+
declare type NEEDLE_progressive_ext = {
|
|
10
10
|
guid: string;
|
|
11
11
|
lods: Array<NEEDLE_progressive_model_LOD>;
|
|
12
12
|
};
|
|
13
|
-
export declare type
|
|
13
|
+
export declare type NEEDLE_ext_progressive_texture = NEEDLE_progressive_ext & {
|
|
14
14
|
lods: Array<NEEDLE_progressive_model_LOD & {
|
|
15
15
|
width: number;
|
|
16
16
|
height: number;
|
|
17
17
|
}>;
|
|
18
18
|
};
|
|
19
|
-
export declare type
|
|
19
|
+
export declare type NEEDLE_ext_progressive_mesh = NEEDLE_progressive_ext & {
|
|
20
|
+
/** @deprecated Removed */
|
|
20
21
|
density: number;
|
|
21
22
|
lods: Array<NEEDLE_progressive_model_LOD & {
|
|
23
|
+
/** @deprecated Use densities with the primitive index */
|
|
22
24
|
density: number;
|
|
23
25
|
indexCount: number;
|
|
24
26
|
vertexCount: number;
|
|
27
|
+
densities: number[] | undefined;
|
|
25
28
|
}>;
|
|
26
29
|
};
|
|
27
30
|
/**
|
|
@@ -64,7 +67,8 @@ declare type TextureLODsMinMaxInfo = {
|
|
|
64
67
|
export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
65
68
|
/** The name of the extension */
|
|
66
69
|
get name(): string;
|
|
67
|
-
static
|
|
70
|
+
static getMeshLODExtension(geo: BufferGeometry): NEEDLE_ext_progressive_mesh | null;
|
|
71
|
+
static getPrimitiveIndex(geo: BufferGeometry): number;
|
|
68
72
|
static getMaterialMinMaxLODsCount(material: Material | Material[], minmax?: TextureLODsMinMaxInfo): TextureLODsMinMaxInfo;
|
|
69
73
|
/** Check if a LOD level is available for a mesh or a texture
|
|
70
74
|
* @param obj the mesh or texture to check
|
|
@@ -107,11 +111,11 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
107
111
|
/**
|
|
108
112
|
* Register a texture with LOD information
|
|
109
113
|
*/
|
|
110
|
-
static registerTexture: (url: string, tex: Texture, level: number, index: number, ext:
|
|
114
|
+
static registerTexture: (url: string, tex: Texture, level: number, index: number, ext: NEEDLE_ext_progressive_texture) => void;
|
|
111
115
|
/**
|
|
112
116
|
* Register a mesh with LOD information
|
|
113
117
|
*/
|
|
114
|
-
static registerMesh: (url: string, key: string, mesh: Mesh, level: number, index: number
|
|
118
|
+
static registerMesh: (url: string, key: string, mesh: Mesh, level: number, index: number, ext: NEEDLE_ext_progressive_mesh) => void;
|
|
115
119
|
/** A map of key = asset uuid and value = LOD information */
|
|
116
120
|
private static readonly lodInfos;
|
|
117
121
|
/** cache of already loaded mesh lods */
|
|
@@ -1,80 +1,15 @@
|
|
|
1
1
|
import { BufferGeometry, Mesh, Texture, TextureLoader } from "three";
|
|
2
2
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
3
|
import { addDracoAndKTX2Loaders } from "./loaders.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveUrl } from "./utils.internal.js";
|
|
5
5
|
import { getRaycastMesh, registerRaycastMesh } from "./utils.js";
|
|
6
6
|
// All of this has to be removed
|
|
7
7
|
// import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
|
|
8
8
|
// import { PromiseAllWithErrors, resolveUrl } from "../../engine_utils.js";
|
|
9
9
|
import { plugins } from "./plugins/plugin.js";
|
|
10
|
+
import { debug } from "./lods.debug.js";
|
|
10
11
|
export const EXTENSION_NAME = "NEEDLE_progressive";
|
|
11
|
-
const debug = getParam("debugprogressive");
|
|
12
12
|
const $progressiveTextureExtension = Symbol("needle-progressive-texture");
|
|
13
|
-
const debug_toggle_maps = new Map();
|
|
14
|
-
const debug_materials = new Set();
|
|
15
|
-
if (debug) {
|
|
16
|
-
let currentDebugLodLevel = -1;
|
|
17
|
-
let maxLevel = 2;
|
|
18
|
-
let wireframe = false;
|
|
19
|
-
function debugToggleProgressive() {
|
|
20
|
-
currentDebugLodLevel += 1;
|
|
21
|
-
console.log("Toggle LOD level", currentDebugLodLevel, debug_toggle_maps);
|
|
22
|
-
debug_toggle_maps.forEach((arr, obj) => {
|
|
23
|
-
for (const key of arr.keys) {
|
|
24
|
-
const cur = obj[key];
|
|
25
|
-
// if it's null or undefined we skip it
|
|
26
|
-
if (cur == null) {
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (cur.isBufferGeometry === true) {
|
|
30
|
-
const info = NEEDLE_progressive.getMeshLODInformation(cur);
|
|
31
|
-
const level = !info ? 0 : Math.min(currentDebugLodLevel, info.lods.length);
|
|
32
|
-
obj["DEBUG:LOD"] = level;
|
|
33
|
-
// NEEDLE_progressive.assignMeshLOD(obj as Mesh, level);
|
|
34
|
-
if (info)
|
|
35
|
-
maxLevel = Math.max(maxLevel, info.lods.length - 1);
|
|
36
|
-
}
|
|
37
|
-
else if (obj.isMaterial === true) {
|
|
38
|
-
obj["DEBUG:LOD"] = currentDebugLodLevel;
|
|
39
|
-
// NEEDLE_progressive.assignTextureLOD(obj as Material, currentDebugLodLevel);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
if (currentDebugLodLevel >= maxLevel) {
|
|
44
|
-
currentDebugLodLevel = -1;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
window.addEventListener("keyup", evt => {
|
|
48
|
-
if (evt.key === "p")
|
|
49
|
-
debugToggleProgressive();
|
|
50
|
-
if (evt.key === "w") {
|
|
51
|
-
wireframe = !wireframe;
|
|
52
|
-
if (debug_materials) {
|
|
53
|
-
debug_materials.forEach(mat => {
|
|
54
|
-
// we don't want to change the skybox material
|
|
55
|
-
if (mat.name == "BackgroundCubeMaterial")
|
|
56
|
-
return;
|
|
57
|
-
if (mat["glyphMap"] != undefined)
|
|
58
|
-
return;
|
|
59
|
-
if ("wireframe" in mat) {
|
|
60
|
-
mat.wireframe = wireframe;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
function registerDebug(obj, key, sourceId) {
|
|
68
|
-
if (!debug)
|
|
69
|
-
return;
|
|
70
|
-
if (!debug_toggle_maps.has(obj)) {
|
|
71
|
-
debug_toggle_maps.set(obj, { keys: [], sourceId });
|
|
72
|
-
}
|
|
73
|
-
const existing = debug_toggle_maps.get(obj);
|
|
74
|
-
if (existing?.keys?.includes(key) == false) {
|
|
75
|
-
existing.keys.push(key);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
13
|
/**
|
|
79
14
|
* The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
|
|
80
15
|
* This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
|
|
@@ -95,13 +30,19 @@ export class NEEDLE_progressive {
|
|
|
95
30
|
get name() {
|
|
96
31
|
return EXTENSION_NAME;
|
|
97
32
|
}
|
|
98
|
-
static
|
|
33
|
+
static getMeshLODExtension(geo) {
|
|
99
34
|
const info = this.getAssignedLODInformation(geo);
|
|
100
35
|
if (info?.key) {
|
|
101
36
|
return this.lodInfos.get(info.key);
|
|
102
37
|
}
|
|
103
38
|
return null;
|
|
104
39
|
}
|
|
40
|
+
static getPrimitiveIndex(geo) {
|
|
41
|
+
const index = this.getAssignedLODInformation(geo)?.index;
|
|
42
|
+
if (index === undefined || index === null)
|
|
43
|
+
return -1;
|
|
44
|
+
return index;
|
|
45
|
+
}
|
|
105
46
|
static getMaterialMinMaxLODsCount(material, minmax) {
|
|
106
47
|
const self = this;
|
|
107
48
|
// we can cache this material min max data because it wont change at runtime
|
|
@@ -261,8 +202,6 @@ export class NEEDLE_progressive {
|
|
|
261
202
|
// if (debug == "verbose") console.log("Progressive Mesh " + mesh.name + " loaded", currentGeometry, "→", geo, "\n", mesh)
|
|
262
203
|
if (isGeometry) {
|
|
263
204
|
mesh.geometry = geo;
|
|
264
|
-
if (debug)
|
|
265
|
-
registerDebug(mesh, "geometry", lodinfo.url);
|
|
266
205
|
}
|
|
267
206
|
else if (debug) {
|
|
268
207
|
console.error("Invalid LOD geometry", geo);
|
|
@@ -311,8 +250,6 @@ export class NEEDLE_progressive {
|
|
|
311
250
|
const material = materialOrTexture;
|
|
312
251
|
const promises = [];
|
|
313
252
|
const slots = new Array();
|
|
314
|
-
if (debug)
|
|
315
|
-
debug_materials.add(material);
|
|
316
253
|
// Handle custom shaders / uniforms progressive textures. This includes support for VRM shaders
|
|
317
254
|
if (material.uniforms && (material.isRawShaderMaterial || material.isShaderMaterial === true)) {
|
|
318
255
|
// iterate uniforms of custom shaders
|
|
@@ -391,13 +328,6 @@ export class NEEDLE_progressive {
|
|
|
391
328
|
}
|
|
392
329
|
material[slot] = tex;
|
|
393
330
|
}
|
|
394
|
-
if (debug && slot && material) {
|
|
395
|
-
const lodinfo = this.getAssignedLODInformation(current);
|
|
396
|
-
if (lodinfo)
|
|
397
|
-
registerDebug(material, slot, lodinfo.url);
|
|
398
|
-
else
|
|
399
|
-
console.warn("No LOD info for texture", current);
|
|
400
|
-
}
|
|
401
331
|
// check if the old texture is still used by other objects
|
|
402
332
|
// if not we dispose it...
|
|
403
333
|
// this could also be handled elsewhere and not be done immediately
|
|
@@ -440,7 +370,7 @@ export class NEEDLE_progressive {
|
|
|
440
370
|
return this.parser.getDependency("mesh", meshIndex).then(mesh => {
|
|
441
371
|
this._isLoadingMesh = false;
|
|
442
372
|
if (mesh) {
|
|
443
|
-
NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh, ext.lods?.length,
|
|
373
|
+
NEEDLE_progressive.registerMesh(this.url, ext.guid, mesh, ext.lods?.length, 0, ext);
|
|
444
374
|
}
|
|
445
375
|
return mesh;
|
|
446
376
|
});
|
|
@@ -522,7 +452,7 @@ export class NEEDLE_progressive {
|
|
|
522
452
|
if (tex.source)
|
|
523
453
|
tex.source[$progressiveTextureExtension] = ext;
|
|
524
454
|
const LODKEY = ext.guid;
|
|
525
|
-
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, level, index
|
|
455
|
+
NEEDLE_progressive.assignLODInformation(url, tex, LODKEY, level, index);
|
|
526
456
|
NEEDLE_progressive.lodInfos.set(LODKEY, ext);
|
|
527
457
|
NEEDLE_progressive.lowresCache.set(LODKEY, tex);
|
|
528
458
|
};
|
|
@@ -530,8 +460,6 @@ export class NEEDLE_progressive {
|
|
|
530
460
|
* Register a mesh with LOD information
|
|
531
461
|
*/
|
|
532
462
|
static registerMesh = (url, key, mesh, level, index, ext) => {
|
|
533
|
-
if (debug)
|
|
534
|
-
console.log("> Progressive: register mesh", index, mesh.name, ext, mesh.uuid, mesh);
|
|
535
463
|
const geometry = mesh.geometry;
|
|
536
464
|
if (!geometry) {
|
|
537
465
|
if (debug)
|
|
@@ -540,7 +468,9 @@ export class NEEDLE_progressive {
|
|
|
540
468
|
}
|
|
541
469
|
if (!geometry.userData)
|
|
542
470
|
geometry.userData = {};
|
|
543
|
-
|
|
471
|
+
if (debug)
|
|
472
|
+
console.log("> Progressive: register mesh " + mesh.name, { index, uuid: mesh.uuid }, ext, mesh);
|
|
473
|
+
NEEDLE_progressive.assignLODInformation(url, geometry, key, level, index);
|
|
544
474
|
NEEDLE_progressive.lodInfos.set(key, ext);
|
|
545
475
|
let existing = NEEDLE_progressive.lowresCache.get(key);
|
|
546
476
|
if (existing)
|
|
@@ -695,7 +625,7 @@ export class NEEDLE_progressive {
|
|
|
695
625
|
if (found) {
|
|
696
626
|
let tex = await parser.getDependency("texture", index);
|
|
697
627
|
if (tex) {
|
|
698
|
-
NEEDLE_progressive.assignLODInformation(LOD.url, tex, LODKEY, level, undefined
|
|
628
|
+
NEEDLE_progressive.assignLODInformation(LOD.url, tex, LODKEY, level, undefined);
|
|
699
629
|
}
|
|
700
630
|
if (debugverbose)
|
|
701
631
|
console.log("change \"" + current.name + "\" → \"" + tex.name + "\"", lod_url, index, tex, KEY);
|
|
@@ -733,7 +663,7 @@ export class NEEDLE_progressive {
|
|
|
733
663
|
console.log(`Loaded Mesh \"${mesh.name}\"`, lod_url, index, mesh, KEY);
|
|
734
664
|
if (mesh.isMesh === true) {
|
|
735
665
|
const geo = mesh.geometry;
|
|
736
|
-
NEEDLE_progressive.assignLODInformation(LOD.url, geo, LODKEY, level,
|
|
666
|
+
NEEDLE_progressive.assignLODInformation(LOD.url, geo, LODKEY, level, 0);
|
|
737
667
|
return resolve(geo);
|
|
738
668
|
}
|
|
739
669
|
else {
|
|
@@ -742,7 +672,7 @@ export class NEEDLE_progressive {
|
|
|
742
672
|
const child = mesh.children[i];
|
|
743
673
|
if (child.isMesh === true) {
|
|
744
674
|
const geo = child.geometry;
|
|
745
|
-
NEEDLE_progressive.assignLODInformation(LOD.url, geo, LODKEY, level, i
|
|
675
|
+
NEEDLE_progressive.assignLODInformation(LOD.url, geo, LODKEY, level, i);
|
|
746
676
|
geometries.push(geo);
|
|
747
677
|
}
|
|
748
678
|
}
|
|
@@ -786,12 +716,12 @@ export class NEEDLE_progressive {
|
|
|
786
716
|
}
|
|
787
717
|
return null;
|
|
788
718
|
}
|
|
789
|
-
static assignLODInformation(url, res, key, level, index
|
|
719
|
+
static assignLODInformation(url, res, key, level, index) {
|
|
790
720
|
if (!res)
|
|
791
721
|
return;
|
|
792
722
|
if (!res.userData)
|
|
793
723
|
res.userData = {};
|
|
794
|
-
const info = new LODInformation(url, key, level, index
|
|
724
|
+
const info = new LODInformation(url, key, level, index);
|
|
795
725
|
res.userData.LODS = info;
|
|
796
726
|
}
|
|
797
727
|
static getAssignedLODInformation(res) {
|
|
@@ -848,16 +778,12 @@ class LODInformation {
|
|
|
848
778
|
level;
|
|
849
779
|
/** For multi objects (e.g. a group of meshes) this is the index of the object */
|
|
850
780
|
index;
|
|
851
|
-
|
|
852
|
-
density;
|
|
853
|
-
constructor(url, key, level, index, density) {
|
|
781
|
+
constructor(url, key, level, index) {
|
|
854
782
|
this.url = url;
|
|
855
783
|
this.key = key;
|
|
856
784
|
this.level = level;
|
|
857
785
|
if (index != undefined)
|
|
858
786
|
this.index = index;
|
|
859
|
-
if (density != undefined)
|
|
860
|
-
this.density = density;
|
|
861
787
|
}
|
|
862
788
|
}
|
|
863
789
|
;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getParam } from "./utils.internal.js";
|
|
2
|
+
export const debug = getParam("debugprogressive");
|
|
3
|
+
let debug_RenderWireframe;
|
|
4
|
+
export let debug_OverrideLodLevel = -1; // -1 is automatic
|
|
5
|
+
if (debug) {
|
|
6
|
+
let maxLevel = 6;
|
|
7
|
+
function debugToggleProgressive() {
|
|
8
|
+
debug_OverrideLodLevel += 1;
|
|
9
|
+
if (debug_OverrideLodLevel >= maxLevel) {
|
|
10
|
+
debug_OverrideLodLevel = -1;
|
|
11
|
+
}
|
|
12
|
+
console.log(`Toggle LOD level [${debug_OverrideLodLevel}]`);
|
|
13
|
+
}
|
|
14
|
+
window.addEventListener("keyup", evt => {
|
|
15
|
+
if (evt.key === "p")
|
|
16
|
+
debugToggleProgressive();
|
|
17
|
+
if (evt.key === "w") {
|
|
18
|
+
debug_RenderWireframe = !debug_RenderWireframe;
|
|
19
|
+
console.log(`Toggle wireframe [${debug_RenderWireframe}]`);
|
|
20
|
+
}
|
|
21
|
+
const pressedNumber = parseInt(evt.key);
|
|
22
|
+
if (!isNaN(pressedNumber) && pressedNumber >= 0) {
|
|
23
|
+
debug_OverrideLodLevel = pressedNumber;
|
|
24
|
+
console.log(`Set LOD level to [${debug_OverrideLodLevel}]`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function applyDebugSettings(material) {
|
|
29
|
+
if (!debug)
|
|
30
|
+
return;
|
|
31
|
+
if (Array.isArray(material)) {
|
|
32
|
+
for (const mat of material) {
|
|
33
|
+
applyDebugSettings(mat);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (material) {
|
|
37
|
+
if ("wireframe" in material) {
|
|
38
|
+
material.wireframe = debug_RenderWireframe === true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -43,9 +43,14 @@ declare type LODChangedEventListener = (args: {
|
|
|
43
43
|
*/
|
|
44
44
|
export declare class LODsManager {
|
|
45
45
|
#private;
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
|
|
47
48
|
*/
|
|
48
49
|
static debugDrawLine?: (a: Vector3, b: Vector3, color: number) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Force override the LOD level for all objects in the scene
|
|
52
|
+
*/
|
|
53
|
+
static overrideGlobalLodLevel?: number;
|
|
49
54
|
/** @internal */
|
|
50
55
|
static getObjectLODState(object: Object3D): LOD_state | undefined;
|
|
51
56
|
static addPlugin(plugin: NEEDLE_progressive_plugin): void;
|
|
@@ -4,6 +4,7 @@ import { createLoaders } from "./loaders.js";
|
|
|
4
4
|
import { getParam, isMobileDevice } from "./utils.internal.js";
|
|
5
5
|
import { plugins } from "./plugins/plugin.js";
|
|
6
6
|
import { getRaycastMesh } from "./utils.js";
|
|
7
|
+
import { applyDebugSettings, debug, debug_OverrideLodLevel } from "./lods.debug.js";
|
|
7
8
|
const debugProgressiveLoading = getParam("debugprogressive");
|
|
8
9
|
const suppressProgressiveLoading = getParam("noprogressive");
|
|
9
10
|
const $lodsManager = Symbol("Needle:LODSManager");
|
|
@@ -40,9 +41,14 @@ const levels = { mesh_lod: -1, texture_lod: -1 };
|
|
|
40
41
|
* ```
|
|
41
42
|
*/
|
|
42
43
|
export class LODsManager {
|
|
43
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
|
|
44
46
|
*/
|
|
45
47
|
static debugDrawLine;
|
|
48
|
+
/**
|
|
49
|
+
* Force override the LOD level for all objects in the scene
|
|
50
|
+
*/
|
|
51
|
+
static overrideGlobalLodLevel;
|
|
46
52
|
/** @internal */
|
|
47
53
|
static getObjectLODState(object) {
|
|
48
54
|
return object[$lodstate];
|
|
@@ -305,17 +311,26 @@ export class LODsManager {
|
|
|
305
311
|
for (const plugin of plugins) {
|
|
306
312
|
plugin.onBeforeUpdateLOD?.(this.renderer, scene, camera, object);
|
|
307
313
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
314
|
+
const debugLodLevel = LODsManager.overrideGlobalLodLevel !== undefined ? LODsManager.overrideGlobalLodLevel : debug_OverrideLodLevel;
|
|
315
|
+
if (debugLodLevel >= 0) {
|
|
316
|
+
levels.mesh_lod = debugLodLevel;
|
|
317
|
+
levels.texture_lod = debugLodLevel;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.calculateLodLevel(camera, object, state, desiredDensity, levels);
|
|
321
|
+
levels.mesh_lod = Math.round(levels.mesh_lod);
|
|
322
|
+
levels.texture_lod = Math.round(levels.texture_lod);
|
|
323
|
+
}
|
|
311
324
|
// we currently only support auto LOD changes for meshes
|
|
312
325
|
if (levels.mesh_lod >= 0) {
|
|
313
326
|
this.loadProgressiveMeshes(object, levels.mesh_lod);
|
|
314
327
|
}
|
|
315
328
|
// TODO: we currently can not switch texture lods because we need better caching for the textures internally (see copySettings in progressive + NE-4431)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
329
|
+
if (object.material && levels.texture_lod >= 0) {
|
|
330
|
+
this.loadProgressiveTextures(object.material, levels.texture_lod);
|
|
331
|
+
}
|
|
332
|
+
if (debug && object.material && !object["isGizmo"]) {
|
|
333
|
+
applyDebugSettings(object.material);
|
|
319
334
|
}
|
|
320
335
|
for (const plugin of plugins) {
|
|
321
336
|
plugin.onAfterUpdatedLOD?.(this.renderer, scene, camera, object, levels);
|
|
@@ -435,8 +450,8 @@ export class LODsManager {
|
|
|
435
450
|
return mesh["DEBUG:LOD"];
|
|
436
451
|
}
|
|
437
452
|
// The mesh info contains also the density for all available LOD level so we can use this for selecting which level to show
|
|
438
|
-
const
|
|
439
|
-
const
|
|
453
|
+
const mesh_lods = NEEDLE_progressive.getMeshLODExtension(mesh.geometry)?.lods;
|
|
454
|
+
const primitive_index = NEEDLE_progressive.getPrimitiveIndex(mesh.geometry);
|
|
440
455
|
const has_mesh_lods = mesh_lods && mesh_lods.length > 0;
|
|
441
456
|
const texture_lods_minmax = NEEDLE_progressive.getMaterialMinMaxLODsCount(mesh.material);
|
|
442
457
|
const has_texture_lods = texture_lods_minmax?.min_count != Infinity && texture_lods_minmax.min_count > 0 && texture_lods_minmax.max_count > 0;
|
|
@@ -590,7 +605,8 @@ export class LODsManager {
|
|
|
590
605
|
// const framerate = this.context.time.smoothedFps;
|
|
591
606
|
if (mesh_lods && state.lastScreenCoverage > 0) {
|
|
592
607
|
for (let l = 0; l < mesh_lods.length; l++) {
|
|
593
|
-
const
|
|
608
|
+
const lod = mesh_lods[l];
|
|
609
|
+
const densityForThisLevel = lod.densities?.[primitive_index] || lod.density || .00001;
|
|
594
610
|
const resultingDensity = densityForThisLevel / state.lastScreenCoverage;
|
|
595
611
|
if (resultingDensity < desiredDensity) {
|
|
596
612
|
expectedLevel = l;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebGLRenderer, Scene, Camera, Mesh } from 'three';
|
|
2
|
-
import {
|
|
2
|
+
import { NEEDLE_ext_progressive_mesh } from '../extension.js';
|
|
3
3
|
/**
|
|
4
4
|
* This interface is used to define a plugin for the progressive extension. It can be registered using the `registerPlugin` function.
|
|
5
5
|
*/
|
|
@@ -12,7 +12,7 @@ export interface NEEDLE_progressive_plugin {
|
|
|
12
12
|
texture_lod: number;
|
|
13
13
|
}): void;
|
|
14
14
|
/** Called when a new mesh is registered */
|
|
15
|
-
onRegisteredNewMesh?(mesh: Mesh, ext:
|
|
15
|
+
onRegisteredNewMesh?(mesh: Mesh, ext: NEEDLE_ext_progressive_mesh): void;
|
|
16
16
|
/** Called before the LOD mesh is fetched */
|
|
17
17
|
onBeforeGetLODMesh?(mesh: Mesh, level: number): void;
|
|
18
18
|
}
|