@needle-tools/gltf-progressive 1.2.2-alpha.3 → 1.2.3-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/README.md +3 -1
- package/examples/modelviewer-multiple.html +24 -12
- package/gltf-progressive.js +643 -604
- package/gltf-progressive.min.js +7 -6
- package/gltf-progressive.umd.cjs +8 -7
- package/lib/index.js +3 -3
- package/lib/lods_manager.d.ts +4 -4
- package/lib/lods_manager.js +56 -14
- package/lib/plugins/modelviewer.js +41 -6
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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 s=this,o=ee(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),e.call(this,r,n),i&&(s.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((s,o)=>{for(const i of s.keys){const a=o[i];if(a!=null){if(a.isBufferGeometry===!0){const l=w.getMeshLODInformation(a),u=l?Math.min(e,l.lods.length):0;o["DEBUG:LOD"]=e,w.assignMeshLOD(o,u),l&&(r=Math.max(r,l.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,w.assignTextureLOD(o,e);break}}}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(n=!n,de&&de.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=n)}))})}function ye(t,e,r){var n;if(!y)return;X.has(t)||X.set(t,{keys:[],sourceId:r});const s=X.get(t);((n=s?.keys)==null?void 0:n.includes(e))==!1&&s.keys.push(e)}const M=class{constructor(t,e){c(this,"parser"),c(this,"url"),c(this,"_isLoadingMesh"),c(this,"loadMesh",r=>{var n,s;if(this._isLoadingMesh)return null;const o=(s=(n=this.parser.json.meshes[r])==null?void 0:n.extensions)==null?void 0:s[B];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>{var a;return this._isLoadingMesh=!1,i&&M.registerMesh(this.url,o.guid,i,(a=o.lods)==null?void 0:a.length,void 0,o),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",s=t[n];if(s!=null)return s;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&&o(l,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const a=t[i];a?.isTexture===!0&&o(a,e)}return t[n]=e,e;function o(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 f=0;f<u.lods.length;f++){const h=u.lods[f];h.width&&(a.lods[f]=a.lods[f]||{min_height:1/0,max_height:0},a.lods[f].min_height=Math.min(a.lods[f].min_height,h.height),a.lods[f].max_height=Math.max(a.lods[f].max_height,h.height))}}}}}static hasLODLevelAvailable(t,e){var r;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,s;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(r=n?.userData)!=null&&r.LODS){const o=n.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.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,s=this.getAssignedLODInformation(n);if(!s)return Promise.resolve(null);for(const o of j)(r=o.onBeforeGetLODMesh)==null||r.call(o,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(n,e).then(o=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(o)){const i=s.index||0;o=o[i]}o&&n!=o&&(o?.isBufferGeometry?(t.geometry=o,y&&ye(t,"geometry",s.url)):y&&console.error("Invalid LOD geometry",o))}return o}).catch(o=>(console.error("Error loading mesh LOD",t,o),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=[],s=new Array;if(y&&de.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const o=r;for(const i of Object.keys(o.uniforms)){const a=o.uniforms[i].value;if(a?.isTexture===!0){const l=this.assignTextureLODForSlot(a,e,r,i);n.push(l),s.push(i)}}}else for(const o of Object.keys(r)){const i=r[o];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,r,o);n.push(a),s.push(o)}}return Promise.all(n).then(o=>{const i=new Array;for(let a=0;a<o.length;a++){const l=o[a],u=s[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(s=>{if(Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t){if(r&&n){const o=r[n];if(o){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return y==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,o,s),null}r[n]=s}if(y&&n&&r){const o=this.getAssignedLODInformation(t);o&&ye(r,n,o.url)}}return s}else y=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return y&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,s)=>{var o;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===s&&(a=!0,M.registerTexture(this.url,l,(o=i.lods)==null?void 0:o.length,s,i));a||this.parser.getDependency("texture",s).then(l=>{var u;l&&M.registerTexture(this.url,l,(u=i.lods)==null?void 0:u.length,s,i)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,s)=>{if(n!=null&&n.extensions){const o=n?.extensions[B];if(o&&o.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const a=this.parser.associations.get(i);a.meshes===s&&M.registerMesh(this.url,o.guid,i,o.lods.length,a.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var r,n,s,o;const i=y=="verbose",a=t.userData.LODS;if(!a)return null;const l=a?.key;let u;if(t.isTexture===!0){const f=t;f.source&&f.source[ce]&&(u=f.source[ce])}if(u||(u=M.lodInfos.get(l)),u){if(e>0){let g=!1;const S=Array.isArray(u.lods);if(S&&e>=u.lods.length?g=!0:S||(g=!0),g)return this.lowresCache.get(l)}const f=Array.isArray(u.lods)?(r=u.lods[e])==null?void 0:r.path:u.lods;if(!f)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,f);if(h.endsWith(".glb")||h.endsWith(".gltf")){if(!u.guid)return console.warn("missing pointer for glb/gltf texture",u),null;const g=h+"_"+u.guid,S=this.previouslyLoaded.get(g);if(S!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${g}`);let m=await S.catch(b=>(console.error(`Error loading LOD ${e} from ${h}
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
1
|
+
var Re=Object.defineProperty,Ge=(t,e,s)=>e in t?Re(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s,d=(t,e,s)=>(Ge(t,typeof e!="symbol"?e+"":e,s),s),Oe=(t,e,s)=>{if(!e.has(t))throw TypeError("Cannot "+s)},y=(t,e,s)=>(Oe(t,e,"read from private field"),s?s.call(t):e.get(t)),V=(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)},G=(t,e,s,o)=>(Oe(t,e,"write to private field"),o?o.call(t,s):e.set(t,s),s);import{BufferGeometry as oe,Mesh as K,Material as Fe,Texture as Y,TextureLoader as We,Matrix4 as Se,Frustum as Ne,Clock as $e,MeshStandardMaterial as ze,Sphere as Ue,Box3 as Te,Vector3 as F}from"three";import{GLTFLoader as Ve}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as qe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as Xe}from"three/examples/jsm/loaders/KTX2Loader.js";const ge="";globalThis.GLTF_PROGRESSIVE_VERSION=ge,console.debug(`[gltf-progressive] version ${ge}`);let ie="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",me="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(ie+"draco_decoder.js",{method:"head"}).catch(t=>{ie="./include/draco/",me="./include/ktx2/"});function He(t){ie=t}function Ye(t){me=t}let J,pe,Q;function xe(t){J||(J=new Ke,J.setDecoderPath(ie),J.setDecoderConfig({type:"js"})),Q||(Q=new Xe,Q.setTranscoderPath(me)),pe||(pe=qe),t?Q.detectSupport(t):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function ye(t){t.dracoLoader||t.setDRACOLoader(J),t.ktx2Loader||t.setKTX2Loader(Q),t.meshoptDecoder||t.setMeshoptDecoder(pe)}Z("debugprogressive");function Z(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Je(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const s=t.lastIndexOf("/");if(s>=0){const o=t.substring(0,s+1);for(;o.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return o+e}return e}let le;function Qe(){return le!==void 0||(le=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),Z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",le)),le}const ve=Symbol("needle:raycast-mesh");function ae(t){return t?.[ve]instanceof oe?t[ve]:null}function Ee(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!ae(t)){const s=et(e);s.userData={isRaycastMesh:!0},t[ve]=s}}function Ze(t=!0){if(t){if(ee)return;const e=ee=K.prototype.raycast;K.prototype.raycast=function(s,o){const r=this,n=ae(r);let i;n&&r.isMesh&&(i=r.geometry,r.geometry=n),e.call(this,s,o),i&&(r.geometry=i)}}else{if(!ee)return;K.prototype.raycast=ee,ee=null}}let ee=null;function et(t){const e=new oe;for(const s in t.attributes)e.setAttribute(s,t.getAttribute(s));return e.setIndex(t.getIndex()),e}const N=new Array,j="NEEDLE_progressive",v=Z("debugprogressive"),Le=Symbol("needle-progressive-texture"),te=new Map,Me=new Set;if(v){let t=function(){e+=1,console.log("Toggle LOD level",e,te),te.forEach((r,n)=>{for(const i of r.keys){const l=n[i];if(l!=null){if(l.isBufferGeometry===!0){const u=b.getMeshLODInformation(l),a=u?Math.min(e,u.lods.length):0;n["DEBUG:LOD"]=e,b.assignMeshLOD(n,a),u&&(s=Math.max(s,u.lods.length-1))}else if(n.isMaterial===!0){n["DEBUG:LOD"]=e,b.assignTextureLOD(n,e);break}}}}),e>=s&&(e=-1)},e=-1,s=2,o=!1;window.addEventListener("keyup",r=>{r.key==="p"&&t(),r.key==="w"&&(o=!o,Me&&Me.forEach(n=>{n.name!="BackgroundCubeMaterial"&&n.glyphMap==null&&"wireframe"in n&&(n.wireframe=o)}))})}function Ae(t,e,s){var o;if(!v)return;te.has(t)||te.set(t,{keys:[],sourceId:s});const r=te.get(t);((o=r?.keys)==null?void 0:o.includes(e))==!1&&r.keys.push(e)}const w=class{constructor(t,e){d(this,"parser"),d(this,"url"),d(this,"_isLoadingMesh"),d(this,"loadMesh",s=>{var o,r;if(this._isLoadingMesh)return null;const n=(r=(o=this.parser.json.meshes[s])==null?void 0:o.extensions)==null?void 0:r[j];return n?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",s).then(i=>{var l;return this._isLoadingMesh=!1,i&&w.registerMesh(this.url,n.guid,i,(l=n.lods)==null?void 0:l.length,void 0,n),i})):null}),v&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return j}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];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[o]=e,e}if(v==="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&&n(u,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const l=t[i];l?.isTexture===!0&&n(l,e)}return t[o]=e,e;function n(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 p=0;p<a.lods.length;p++){const c=a.lods[p];c.width&&(l.lods[p]=l.lods[p]||{min_height:1/0,max_height:0},l.lods[p].min_height=Math.min(l.lods[p].min_height,c.height),l.lods[p].max_height=Math.max(l.lods[p].max_height,c.height))}}}}}static hasLODLevelAvailable(t,e){var s;if(Array.isArray(t)){for(const n of t)if(this.hasLODLevelAvailable(n,e))return!0;return!1}if(t.isMaterial===!0){for(const n of Object.keys(t)){const i=t[n];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const n of t.children)if(n.isMesh===!0&&this.hasLODLevelAvailable(n,e))return!0}let o,r;if(t.isMesh?o=t.geometry:(t.isBufferGeometry||t.isTexture)&&(o=t),o&&(s=o?.userData)!=null&&s.LODS){const n=o.userData.LODS;if(r=this.lodInfos.get(n.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 K||t.isMesh===!0){const o=t.geometry,r=this.getAssignedLODInformation(o);if(!r)return Promise.resolve(null);for(const n of N)(s=n.onBeforeGetLODMesh)==null||s.call(n,t,e);return t["LOD:requested level"]=e,w.getOrLoadLOD(o,e).then(n=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(n)){const i=r.index||0;n=n[i]}n&&o!=n&&(n?.isBufferGeometry?(t.geometry=n,v&&Ae(t,"geometry",r.url)):v&&console.error("Invalid LOD geometry",n))}return n}).catch(n=>(console.error("Error loading mesh LOD",t,n),null))}else v&&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 Fe||t.isMaterial===!0){const s=t,o=[],r=new Array;if(v&&Me.add(s),s.uniforms&&s.isRawShaderMaterial||s.isShaderMaterial===!0){const n=s;for(const i of Object.keys(n.uniforms)){const l=n.uniforms[i].value;if(l?.isTexture===!0){const u=this.assignTextureLODForSlot(l,e,s,i);o.push(u),r.push(i)}}}else for(const n of Object.keys(s)){const i=s[n];if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,s,n);o.push(l),r.push(n)}}return Promise.all(o).then(n=>{const i=new Array;for(let l=0;l<n.length;l++){const u=n[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 Y||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):w.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if(r?.isTexture===!0){if(r!=t){if(s&&o){const n=s[o];if(n){const i=this.getAssignedLODInformation(n);if(i&&i?.level<e)return v==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,s,n,r),null}s[o]=r}if(v&&o&&s){const n=this.getAssignedLODInformation(t);n&&Ae(s,o,n.url)}}return r}else v=="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 v&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((o,r)=>{var n;if(o!=null&&o.extensions){const i=o?.extensions[j];if(i){if(!i.lods){v&&console.warn("Texture has no LODs",i);return}let l=!1;for(const u of this.parser.associations.keys())u.isTexture===!0&&this.parser.associations.get(u).textures===r&&(l=!0,w.registerTexture(this.url,u,(n=i.lods)==null?void 0:n.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((o,r)=>{if(o!=null&&o.extensions){const n=o?.extensions[j];if(n&&n.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,n.guid,i,n.lods.length,l.primitives,n)}}}}),null}static async getOrLoadLOD(t,e){var s,o,r,n;const i=v=="verbose",l=t.userData.LODS;if(!l)return null;const u=l?.key;let a;if(t.isTexture===!0){const p=t;p.source&&p.source[Le]&&(a=p.source[Le])}if(a||(a=w.lodInfos.get(u)),a){if(e>0){let f=!1;const S=Array.isArray(a.lods);if(S&&e>=a.lods.length?f=!0:S||(f=!0),f)return this.lowresCache.get(u)}const p=Array.isArray(a.lods)?(s=a.lods[e])==null?void 0:s.path:a.lods;if(!p)return v&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const c=Je(l.url,p);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const f=c+"_"+a.guid,S=this.previouslyLoaded.get(f);if(S!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let g=await S.catch(E=>(console.error(`Error loading LOD ${e} from ${c}
|
|
2
|
+
`,E),null)),x=!1;if(g==null||(g instanceof Y&&t instanceof Y?(o=g.image)!=null&&o.data||(r=g.source)!=null&&r.data?g=this.copySettings(t,g):(x=!0,this.previouslyLoaded.delete(f)):g instanceof oe&&t instanceof oe&&((n=g.attributes.position)!=null&&n.array||(x=!0,this.previouslyLoaded.delete(f)))),!x)return g}const M=a,A=new Promise(async(g,x)=>{const E=new Ve;ye(E),v&&(await new Promise(m=>setTimeout(m,1e3)),i&&console.warn("Start loading (delayed) "+c,M.guid));let I=c;if(M&&Array.isArray(M.lods)){const m=M.lods[e];m.hash&&(I+="?v="+m.hash)}const D=await E.loadAsync(I).catch(m=>(console.error(`Error loading LOD ${e} from ${c}
|
|
3
|
+
`,m),null));if(!D)return null;const q=D.parser;i&&console.log("Loading finished "+c,M.guid);let B=0;if(D.parser.json.textures){let m=!1;for(const h of D.parser.json.textures){if(h!=null&&h.extensions){const L=h?.extensions[j];if(L!=null&&L.guid&&L.guid===M.guid){m=!0;break}}B++}if(m){let h=await q.getDependency("texture",B);return h&&w.assignLODInformation(l.url,h,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+h.name+'"',c,B,h,f),t instanceof Y&&(h=this.copySettings(t,h)),h&&(h.guid=M.guid),g(h)}else v&&console.warn("Could not find texture with guid",M.guid,D.parser.json)}if(B=0,D.parser.json.meshes){let m=!1;for(const h of D.parser.json.meshes){if(h!=null&&h.extensions){const L=h?.extensions[j];if(L!=null&&L.guid&&L.guid===M.guid){m=!0;break}}B++}if(m){const h=await q.getDependency("mesh",B),L=M;if(i&&console.log(`Loaded Mesh "${h.name}"`,c,B,h,f),h.isMesh===!0){const _=h.geometry;return w.assignLODInformation(l.url,_,u,e,void 0,L.density),g(_)}else{const _=new Array;for(let O=0;O<h.children.length;O++){const W=h.children[O];if(W.isMesh===!0){const U=W.geometry;w.assignLODInformation(l.url,U,u,e,O,L.density),_.push(U)}}return g(_)}}else v&&console.warn("Could not find mesh with guid",M.guid,D.parser.json)}return g(null)});return this.previouslyLoaded.set(f,A),await A}else if(t instanceof Y){i&&console.log("Load texture from uri: "+c);const f=await new We().loadAsync(c);return f?(f.guid=a.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=t.colorSpace,i&&console.log(a,f)):v&&console.warn("failed loading",c),f}}else v&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,s,o,r,n){if(!e)return;e.userData||(e.userData={});const i=new tt(t,s,o,r,n);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(),v&&console.warn(`Copying texture settings
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e}};let w=M;c(w,"registerTexture",(t,e,r,n,s)=>{if(y&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,s),!e){y&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ce]=s);const o=s.guid;M.assignLODInformation(t,e,o,r,n,void 0),M.lodInfos.set(o,s),M.lowresCache.set(o,e)}),c(w,"registerMesh",(t,e,r,n,s,o)=>{var i;y&&console.log("> Progressive: register mesh",s,r.name,o,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,s,o.density),M.lodInfos.set(e,o);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,o)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map);class Fe{constructor(e,r,n,s,o){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=n,s!=null&&(this.index=s),o!=null&&(this.density=o)}}const $=q("debugprogressive"),Ne=q("noprogressive"),he=Symbol("Needle:LODSManager"),ge=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,"_lodchangedlisteners",[]),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[ge]}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}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}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 s=e._frame,o=t++;e.onBeforeRender(r,n,o,s),e._originalRender.call(this,r,n),e.onAfterRender(r,n,o,s)}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onBeforeRender(t,e,r,n){}onAfterRender(t,e,r,n){var s,o;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&&(((s=h.geometry)==null?void 0:s.type)==="BoxGeometry"||((o=h.geometry)==null?void 0:o.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 g=h.object;(g instanceof N||g.isMesh)&&this.updateLODs(t,e,g,u,n)}const f=i.transparent;for(const h of f){const g=h.object;(g instanceof N||g.isMesh)&&this.updateLODs(t,e,g,u,n)}}}updateLODs(t,e,r,n,s){var o,i;r.userData||(r.userData={});let a=r[ge];if(a||(a=new We,r[ge]=a),a.frames++<2)return;for(const u of j)(o=u.onBeforeUpdateLOD)==null||o.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).then(n=>{this._lodchangedlisteners.forEach(s=>s({type:"texture",level:e,object:t}))}))}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&&this._lodchangedlisteners.forEach(s=>s({type:"mesh",level:e,object:t})),n))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,o=(r.y+n.y)*.5;return this._tempPtInside.set(s,o,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,n,s){var o,i;if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.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),f=u?.lods,h=f&&f.length>0,g=w.getMaterialMinMaxLODsCount(e.material),S=g?.min_count!=1/0&&g.min_count>0&&g.max_count>0;if(!h&&!S){s.mesh_lod=0,s.texture_lod=0;return}if(h||(l=!0,a=0),!((o=this.cameraFrustrum)!=null&&o.intersectsObject(e))){s.mesh_lod=99,s.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)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(T),this._tempBox.applyMatrix4(e.matrixWorld),A.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.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(f&&r.lastScreenCoverage>0){for(let p=0;p<f.length;p++)if(f[p].density/r.lastScreenCoverage<n){I=p;break}}I<a&&(a=I,l=!0)}if(l?s.mesh_lod=a:s.mesh_lod=r.lastLodLevel_Mesh,$&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=f?.[s.mesh_lod];m&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (${m.density.toFixed(0)}) - ${e.name}`)}if(S)if(r.lastLodLevel_Texture<0){if(s.texture_lod=g.max_count-1,$){const m=g.lods[g.max_count-1];$&&console.log(`First Texture LOD ${s.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=g.lods.length-1;E>=0;E--){let L=g.lods[E];if(!(je()&&L.max_height>4096)&&L.max_height>b){if(s.texture_lod=E,s.texture_lod<r.lastLodLevel_Texture){const F=L.max_height;$&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${F}px
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${e.name}`)}break}}}else
|
|
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 b=w;d(b,"registerTexture",(t,e,s,o,r)=>{if(v&&console.log("> Progressive: register texture",o,e.name,e.uuid,e,r),!e){v&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[Le]=r);const n=r.guid;w.assignLODInformation(t,e,n,s,o,void 0),w.lodInfos.set(n,r),w.lowresCache.set(n,e)}),d(b,"registerMesh",(t,e,s,o,r,n)=>{var i;v&&console.log("> Progressive: register mesh",r,s.name,n,s.uuid,s);const l=s.geometry;if(!l){v&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),w.assignLODInformation(t,l,e,o,r,n.density),w.lodInfos.set(e,n);let u=w.lowresCache.get(e);u?u.push(s.geometry):u=[s.geometry],w.lowresCache.set(e,u),o>0&&!ae(s)&&Ee(s,l);for(const a of N)(i=a.onRegisteredNewMesh)==null||i.call(a,s,n)}),d(b,"lodInfos",new Map),d(b,"previouslyLoaded",new Map),d(b,"lowresCache",new Map);class tt{constructor(e,s,o,r,n){d(this,"url"),d(this,"key"),d(this,"level"),d(this,"index"),d(this,"density"),this.url=e,this.key=s,this.level=o,r!=null&&(this.index=r),n!=null&&(this.density=n)}}const C=Z("debugprogressive"),st=Z("noprogressive"),De=Symbol("Needle:LODSManager"),we=Symbol("Needle:LODState"),X=Symbol("Needle:CurrentLOD"),k={mesh_lod:-1,texture_lod:-1};var T,$,_e,H,se,ue,z;const P=class{constructor(t,e){d(this,"context"),d(this,"renderer"),d(this,"projectionScreenMatrix",new Se),d(this,"cameraFrustrum",new Ne),d(this,"targetTriangleDensity",2e5),d(this,"updateInterval","auto"),V(this,T,1),d(this,"pause",!1),d(this,"_lodchangedlisteners",[]),V(this,$,void 0),V(this,_e,new $e),V(this,H,0),V(this,se,0),V(this,ue,0),V(this,z,0),d(this,"_fpsBuffer",[60,60,60,60,60]),d(this,"_sphere",new Ue),d(this,"_tempBox",new Te),d(this,"_tempBox2",new Te),d(this,"tempMatrix",new Se),d(this,"_tempWorldPosition",new F),d(this,"_tempBoxSize",new F),d(this,"_tempBox2Size",new F),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[we]}static addPlugin(t){N.push(t)}static removePlugin(t){const e=N.indexOf(t);e>=0&&N.splice(e,1)}static get(t,e){if(t[De])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[De];const s=new P(t,{engine:"unknown",...e});return t[De]=s,s}get plugins(){return N}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}enable(){if(y(this,$))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;G(this,$,this.renderer.render);const e=this;xe(this.renderer),this.renderer.render=function(s,o){e.renderer.getRenderTarget()==null&&(t=0,G(e,H,y(e,H)+1),G(e,se,y(e,_e).getDelta()),G(e,ue,y(e,ue)+y(e,se)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/y(e,se)),G(e,z,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),C&&y(e,H)%30===0&&console.log("FPS",Math.round(y(e,z)),"Interval:",y(e,T)));const r=y(e,H),n=t++;e.onBeforeRender(s,o,n,r),y(e,$).call(this,s,o),e.onAfterRender(s,o,n,r)}}disable(){y(this,$)&&(this.renderer.render=y(this,$),G(this,$,void 0))}onBeforeRender(t,e,s,o){}onAfterRender(t,e,s,o){var r,n;if(this.pause)return;const i=this.renderer.renderLists.get(t,0),l=i.opaque;let u=!0;if(l.length===1){const a=l[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(u=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(u=!1),u){if(st||(this.updateInterval==="auto"?y(this,z)<40&&y(this,T)<10?(G(this,T,y(this,T)+1),C&&console.warn("\u2193 Reducing LOD updates",y(this,T),y(this,z).toFixed(0))):y(this,z)>=80&&y(this,T)>1&&(G(this,T,y(this,T)-1),C&&console.warn("\u2191 Increasing LOD updates",y(this,T),y(this,z).toFixed(0))):G(this,T,this.updateInterval),y(this,T)>0&&o%y(this,T)!=0))return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const a=this.targetTriangleDensity;for(const c of l){if(c.material&&(((r=c.geometry)==null?void 0:r.type)==="BoxGeometry"||((n=c.geometry)==null?void 0:n.type)==="BufferGeometry")&&(c.material.name==="SphericalGaussianBlur"||c.material.name=="BackgroundCubeMaterial"||c.material.name==="CubemapFromEquirect"||c.material.name==="EquirectangularToCubeUV")){C&&(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(C==="color"&&c.material&&!c.object.progressive_debug_color){c.object.progressive_debug_color=!0;const S=Math.random()*16777215,M=new ze({color:S});c.object.material=M}const f=c.object;(f instanceof K||f.isMesh)&&this.updateLODs(t,e,f,a,o)}const p=i.transparent;for(const c of p){const f=c.object;(f instanceof K||f.isMesh)&&this.updateLODs(t,e,f,a,o)}}}updateLODs(t,e,s,o,r){var n,i;s.userData||(s.userData={});let l=s[we];if(l||(l=new rt,s[we]=l),l.frames++<2)return;for(const a of N)(n=a.onBeforeUpdateLOD)==null||n.call(a,this.renderer,t,e,s);this.calculateLodLevel(e,s,l,o,k),k.mesh_lod=Math.round(k.mesh_lod),k.texture_lod=Math.round(k.texture_lod),k.mesh_lod>=0&&this.loadProgressiveMeshes(s,k.mesh_lod);let u=k.texture_lod;if(s.material&&u>=0){const a=s["DEBUG:LOD"];a!=null&&(u=a),this.loadProgressiveTextures(s.material,u)}for(const a of N)(i=a.onAfterUpdatedLOD)==null||i.call(a,this.renderer,t,e,s,k);l.lastLodLevel_Mesh=k.mesh_lod,l.lastLodLevel_Texture=k.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let s=!1;(t[X]===void 0||e<t[X])&&(s=!0),s&&(t[X]=e,b.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(r=>r({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t[X]!==e){t[X]=e;const s=t.geometry;return b.assignMeshLOD(t,e).then(o=>(o&&t[X]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(r=>r({type:"mesh",level:e,object:t})),o))}return Promise.resolve(null)}static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,o,r){var n,i;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let l=10+1,u=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=b.getMeshLODInformation(e.geometry),p=a?.lods,c=p&&p.length>0,f=b.getMaterialMinMaxLODsCount(e.material),S=f?.min_count!=1/0&&f.min_count>0&&f.max_count>0;if(!c&&!S){r.mesh_lod=0,r.texture_lod=0;return}if(c||(u=!0,l=0),!((n=this.cameraFrustrum)!=null&&n.intersectsObject(e))){r.mesh_lod=99,r.texture_lod=99;return}const M=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let A=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const g=e;g.boundingBox||g.computeBoundingBox(),A=g.boundingBox}if(A&&t.isPerspectiveCamera){const g=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)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(A),this._tempBox.applyMatrix4(e.matrixWorld),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&&g.fov>70){const m=this._tempBox.min,h=this._tempBox.max;let L=m.x,_=m.y,O=h.x,W=h.y;const U=2,he=1.5,re=(m.x+h.x)*.5,ne=(m.y+h.y)*.5;L=(L-re)*U+re,_=(_-ne)*U+ne,O=(O-re)*U+re,W=(W-ne)*U+ne;const ke=L<0&&O>0?0:Math.min(Math.abs(m.x),Math.abs(h.x)),je=_<0&&W>0?0:Math.min(Math.abs(m.y),Math.abs(h.y)),fe=Math.max(ke,je);s.lastCentrality=(he-fe)*(he-fe)*(he-fe)}else s.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&M>0&&x.multiplyScalar(M/screen.availHeight),x.x*=g.aspect;const E=t.matrixWorldInverse,I=this._tempBox2;I.copy(A),I.applyMatrix4(e.matrixWorld),I.applyMatrix4(E);const D=I.getSize(this._tempBox2Size),q=Math.max(D.x,D.y);if(Math.max(x.x,x.y)!=0&&q!=0&&(x.z=D.z/Math.max(D.x,D.y)*Math.max(x.x,x.y)),s.lastScreenCoverage=Math.max(x.x,x.y,x.z),s.lastScreenspaceVolume.copy(x),s.lastScreenCoverage*=s.lastCentrality,C&&P.debugDrawLine){const m=this.tempMatrix.copy(this.projectionScreenMatrix);m.invert();const h=P.corner0,L=P.corner1,_=P.corner2,O=P.corner3;h.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=h.x,_.copy(this._tempBox.max),_.y=h.y,O.copy(this._tempBox.max);const W=(h.z+O.z)*.5;h.z=L.z=_.z=O.z=W,h.applyMatrix4(m),L.applyMatrix4(m),_.applyMatrix4(m),O.applyMatrix4(m),P.debugDrawLine(h,L,255),P.debugDrawLine(h,_,255),P.debugDrawLine(L,O,255),P.debugDrawLine(_,O,255)}let B=999;if(p&&s.lastScreenCoverage>0){for(let m=0;m<p.length;m++)if(p[m].density/s.lastScreenCoverage<o){B=m;break}}B<l&&(l=B,u=!0)}if(u?r.mesh_lod=l:r.mesh_lod=s.lastLodLevel_Mesh,C&&r.mesh_lod!=s.lastLodLevel_Mesh){const g=p?.[r.mesh_lod];g&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (${g.density.toFixed(0)}) - ${e.name}`)}if(S)if(s.lastLodLevel_Texture<0){if(r.texture_lod=f.max_count-1,C){const g=f.lods[f.max_count-1];C&&console.log(`First Texture LOD ${r.texture_lod} (${g.max_height}px) - ${e.name}`)}}else{const g=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let x=s.lastScreenCoverage*2;((i=this.context)==null?void 0:i.engine)==="model-viewer"&&(x*=2);const E=M/window.devicePixelRatio*x;for(let I=f.lods.length-1;I>=0;I--){let D=f.lods[I];if(!(Qe()&&D.max_height>4096)&&D.max_height>E){if(r.texture_lod=I,r.texture_lod<s.lastLodLevel_Texture){const q=D.max_height;C&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${q}px
|
|
6
|
+
Screensize: ${E.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${g.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}else r.texture_lod=0}};let R=P;T=new WeakMap,$=new WeakMap,_e=new WeakMap,H=new WeakMap,se=new WeakMap,ue=new WeakMap,z=new WeakMap,d(R,"debugDrawLine"),d(R,"corner0",new F),d(R,"corner1",new F),d(R,"corner2",new F),d(R,"corner3",new F),d(R,"_tempPtInside",new F);class rt{constructor(){d(this,"frames",0),d(this,"lastLodLevel_Mesh",-1),d(this,"lastLodLevel_Texture",-1),d(this,"lastScreenCoverage",0),d(this,"lastScreenspaceVolume",new F),d(this,"lastCentrality",0)}}const Ie=Symbol("NEEDLE_mesh_lod"),ce=Symbol("NEEDLE_texture_lod");let de=null;function be(){const t=nt();t&&(t.mapURLs(function(e){return Pe(),e}),Pe(),de?.disconnect(),de=new MutationObserver(e=>{e.forEach(s=>{s.addedNodes.forEach(o=>{o instanceof HTMLElement&&o.tagName.toLowerCase()==="model-viewer"&&Ce(o)})})}),de.observe(document,{childList:!0,subtree:!0}))}function nt(){return customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),be()}),null)}function Pe(){document.querySelectorAll("model-viewer").forEach(t=>{Ce(t)})}const Be=new WeakSet;let ot=0;function Ce(t){if(!t||Be.has(t))return null;Be.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++ot+`
|
|
8
|
+
`,t.getAttribute("src"));let e=null,s=null,o=null;for(let r=t;r!=null;r=Object.getPrototypeOf(r)){const n=Object.getOwnPropertySymbols(r),i=n.find(a=>a.toString()=="Symbol(renderer)"),l=n.find(a=>a.toString()=="Symbol(scene)"),u=n.find(a=>a.toString()=="Symbol(needsRender)");!e&&i!=null&&(e=t[i].threeRenderer),!s&&l!=null&&(s=t[l]),!o&&u!=null&&(o=t[u])}if(e&&s){let r=function(){if(o){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}o?.call(t)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=R.get(e,{engine:"model-viewer"});return R.addPlugin(new it),n.enable(),n.addEventListener("changed",()=>{o?.call(t)}),t.addEventListener("model-visibility",i=>{i.detail.visible&&o?.call(t)}),t.addEventListener("load",()=>{r()}),()=>{n.disable()}}return null}class it{constructor(){d(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,s,o,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[ce]==!0)return;s[ce]=!0;const o=this.tryGetCurrentGLTF(e),r=this.tryGetCurrentModelViewer(e),n=this.getUrl(r);if(n&&o&&s.material){let i=function(u){var a,p,c;if(u[ce]==!0)return;u[ce]=!0,u.userData&&(u.userData.LOD=-1);const f=Object.keys(u);for(let S=0;S<f.length;S++){const M=f[S],A=u[M];if(A?.isTexture===!0){const g=(p=(a=A.userData)==null?void 0:a.associations)==null?void 0:p.textures;if(g==null)continue;const x=o.parser.json.textures[g];if(!x){console.warn("Texture data not found for texture index "+g);continue}if((c=x?.extensions)!=null&&c[j]){const E=x.extensions[j];E&&n&&b.registerTexture(n,A,E.lods.length,g,E)}}}};const l=s.material;if(Array.isArray(l))for(const u of l)i(u);else i(l)}}tryParseMeshLOD(e,s){var o,r;if(s[Ie]==!0)return;s[Ie]=!0;const n=this.tryGetCurrentModelViewer(e),i=this.getUrl(n);if(!i)return;const l=(r=(o=s.userData)==null?void 0:o.gltfExtensions)==null?void 0:r[j];if(l&&i){const u=s.uuid;b.registerMesh(i,u,s,0,l.lods.length,l)}}}function lt(t,e,s,o){xe(e),ye(s),s.register(n=>new b(n,t));const r=R.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}be();export{j as EXTENSION_NAME,R as LODsManager,b as NEEDLE_progressive,ge as VERSION,ye as addDracoAndKTX2Loaders,xe as createLoaders,ae as getRaycastMesh,be as patchModelViewer,He as setDracoDecoderLocation,Ye as setKTX2TranscoderLocation,Ee as setRaycastMesh,lt as useNeedleProgressive,Ze as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"use strict";var ve=Object.defineProperty;var we=(l,e,t)=>e in l?ve(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var d=(l,e,t)=>(we(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"),fe="";globalThis.GLTF_PROGRESSIVE_VERSION=fe;console.debug(`[gltf-progressive] version ${fe}`);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 Ee(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 Ce(){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 me(l,e){if((l.type==="Mesh"||l.type==="SkinnedMesh")&&!re(l)){const r=Re(e);r.userData={isRaycastMesh:!0},l[ue]=r}}function Be(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 Re(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",L=J("debugprogressive"),oe=Symbol("needle-progressive-texture"),H=new Map,de=new Set;if(L){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,de&&de.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=r)}))})}function ye(l,e,t){var i;if(!L)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});L&&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(L==="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 m=0;m<c.lods.length;m++){const u=c.lods[m];u.width&&(a.lods[m]=a.lods[m]||{min_height:1/0,max_height:0},a.lods[m].min_height=Math.min(a.lods[m].min_height,u.height),a.lods[m].max_height=Math.max(a.lods[m].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,L&&ye(e,"geometry",o.url)):L&&console.error("Invalid LOD geometry",s))}return s}).catch(s=>(console.error("Error loading mesh LOD",e,s),null))}else L&&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(L&&de.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 L==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,t,r,s,o),null}r[i]=o}if(L&&i&&r){const s=this.getAssignedLODInformation(e);s&&ye(r,i,s.url)}}return o}else L=="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 L&&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){L&&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=L=="verbose",i=e.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(e.isTexture===!0){const m=e;m.source&&m.source[oe]&&(s=m.source[oe])}if(s||(s=O.lodInfos.get(o)),s){if(t>0){let x=!1;const v=Array.isArray(s.lods);if(v&&t>=s.lods.length?x=!0:v||(x=!0),x)return this.lowresCache.get(o)}const m=Array.isArray(s.lods)?(n=s.lods[t])==null?void 0:n.path:s.lods;if(!m)return L&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+t,s)),null;const u=Ee(i.url,m);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,v=this.previouslyLoaded.get(x);if(v!==void 0){r&&console.log(`LOD ${t} was already loading/loaded: ${x}`);let y=await v.catch(N=>(console.error(`Error loading LOD ${t} from ${u}
|
|
2
|
-
`,
|
|
3
|
-
`,T),null));if(!
|
|
4
|
-
`,
|
|
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(L&&console.log("> Progressive: register texture",i,t.name,t.uuid,t,o),!t){L&&console.error("gltf-progressive: Register texture without texture");return}t.source&&(t.source[oe]=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;L&&console.log("> Progressive: register mesh",o,r.name,s,r.uuid,r);const n=r.geometry;if(!n){L&&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)&&me(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"),B={mesh_lod:-1,texture_lod:-1},E=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,"_lodchangedlisteners",[]);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 E(e,{engine:"unknown",...t});return e[ne]=r,r}get plugins(){return V}addEventListener(e,t){e==="changed"&&this._lodchangedlisteners.push(t)}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 m=o.transparent;for(const u of m){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,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);let n=B.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,B);s.lastLodLevel_Mesh=B.mesh_lod,s.lastLodLevel_Texture=B.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).then(i=>{this._lodchangedlisteners.forEach(o=>o({type:"texture",level:t,object:e}))}))}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&&this._lodchangedlisteners.forEach(o=>o({type:"mesh",level:t,object:e})),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 R,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,m=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(!m&&!x){o.mesh_lod=0,o.texture_lod=0;return}if(m||(a=!0,n=0),!((R=this.cameraFrustrum)!=null&&R.intersectsObject(t))){o.mesh_lod=99,o.texture_lod=99;return}const v=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),E.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,_=this._tempBox.max;let b=h.x,A=h.y,P=_.x,W=_.y;const Q=2,se=1.5,Z=(h.x+_.x)*.5,j=(h.y+_.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(_.x)),_e=A<0&&W>0?0:Math.min(Math.abs(h.y),Math.abs(_.y)),ie=Math.max(De,_e);r.lastCentrality=(se-ie)*(se-ie)*(se-ie)}else r.lastCentrality=1;const D=this._tempBox.getSize(this._tempBoxSize);D.multiplyScalar(.5),screen.availHeight>0&&v>0&&D.multiplyScalar(v/screen.availHeight),D.x*=y.aspect;const N=e.matrixWorldInverse,k=this._tempBox2;k.copy(M),k.applyMatrix4(t.matrixWorld),k.applyMatrix4(N);const w=k.getSize(this._tempBox2Size),$=Math.max(w.x,w.y);if(Math.max(D.x,D.y)!=0&&$!=0&&(D.z=w.z/Math.max(w.x,w.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&&E.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const _=E.corner0,b=E.corner1,A=E.corner2,P=E.corner3;_.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=_.x,A.copy(this._tempBox.max),A.y=_.y,P.copy(this._tempBox.max);const W=(_.z+P.z)*.5;_.z=b.z=A.z=P.z=W,_.applyMatrix4(h),b.applyMatrix4(h),A.applyMatrix4(h),P.applyMatrix4(h),E.debugDrawLine(_,b,255),E.debugDrawLine(_,A,255),E.debugDrawLine(b,P,255),E.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=v/window.devicePixelRatio*D;for(let w=u.lods.length-1;w>=0;w--){let $=u.lods[w];if(!(Ce()&&$.max_height>4096)&&$.max_height>k){if(o.texture_lod=w,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: ${
|
|
7
|
-
${
|
|
1
|
+
"use strict";var Re=Object.defineProperty;var Ge=(a,t,e)=>t in a?Re(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var d=(a,t,e)=>(Ge(a,typeof t!="symbol"?t+"":t,e),e),be=(a,t,e)=>{if(!t.has(a))throw TypeError("Cannot "+e)};var x=(a,t,e)=>(be(a,t,"read from private field"),e?e.call(a):t.get(a)),K=(a,t,e)=>{if(t.has(a))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(a):t.set(a,e)},$=(a,t,e,r)=>(be(a,t,"write to private field"),r?r.call(a,e):t.set(a,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("three"),Fe=require("three/examples/jsm/loaders/GLTFLoader.js"),Ve=require("three/examples/jsm/libs/meshopt_decoder.module.js"),$e=require("three/examples/jsm/loaders/DRACOLoader.js"),Ne=require("three/examples/jsm/loaders/KTX2Loader.js"),we="";globalThis.GLTF_PROGRESSIVE_VERSION=we;console.debug(`[gltf-progressive] version ${we}`);let ue="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",ve="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(ue+"draco_decoder.js",{method:"head"}).catch(a=>{ue="./include/draco/",ve="./include/ktx2/"});function ze(a){ue=a}function Ue(a){ve=a}let re,Le,se;function _e(a){re||(re=new $e.DRACOLoader,re.setDecoderPath(ue),re.setDecoderConfig({type:"js"})),se||(se=new Ne.KTX2Loader,se.setTranscoderPath(ve)),Le||(Le=Ve.MeshoptDecoder),a?se.detectSupport(a):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}function Oe(a){a.dracoLoader||a.setDRACOLoader(re),a.ktx2Loader||a.setKTX2Loader(se),a.meshoptDecoder||a.setMeshoptDecoder(Le)}ie("debugprogressive");function ie(a){const e=new URL(window.location.href).searchParams.get(a);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function We(a,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||a===void 0)return t;const e=a.lastIndexOf("/");if(e>=0){const r=a.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let Z;function qe(){return Z!==void 0||(Z=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),ie("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",Z)),Z}const Me=Symbol("needle:raycast-mesh");function he(a){return(a==null?void 0:a[Me])instanceof h.BufferGeometry?a[Me]:null}function Ce(a,t){if((a.type==="Mesh"||a.type==="SkinnedMesh")&&!he(a)){const r=Xe(t);r.userData={isRaycastMesh:!0},a[Me]=r}}function Ke(a=!0){if(a){if(ee)return;const t=ee=h.Mesh.prototype.raycast;h.Mesh.prototype.raycast=function(e,r){const i=this,o=he(i);let s;o&&i.isMesh&&(s=i.geometry,i.geometry=o),t.call(this,e,r),s&&(i.geometry=s)}}else{if(!ee)return;h.Mesh.prototype.raycast=ee,ee=null}}let ee=null;function Xe(a){const t=new h.BufferGeometry;for(const e in a.attributes)t.setAttribute(e,a.getAttribute(e));return t.setIndex(a.getIndex()),t}const X=new Array,N="NEEDLE_progressive",M=ie("debugprogressive"),ye=Symbol("needle-progressive-texture"),oe=new Map,De=new Set;if(M){let a=function(){t+=1,console.log("Toggle LOD level",t,oe),oe.forEach((i,o)=>{for(const s of i.keys){const n=o[s];if(n!=null){if(n.isBufferGeometry===!0){const l=b.getMeshLODInformation(n),u=l?Math.min(t,l.lods.length):0;o["DEBUG:LOD"]=t,b.assignMeshLOD(o,u),l&&(e=Math.max(e,l.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=t,b.assignTextureLOD(o,t);break}}}}),t>=e&&(t=-1)},t=-1,e=2,r=!1;window.addEventListener("keyup",i=>{i.key==="p"&&a(),i.key==="w"&&(r=!r,De&&De.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=r)}))})}function Te(a,t,e){var i;if(!M)return;oe.has(a)||oe.set(a,{keys:[],sourceId:e});const r=oe.get(a);((i=r==null?void 0:r.keys)==null?void 0:i.includes(t))==!1&&r.keys.push(t)}const 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[N];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,void 0,e),o})):null});M&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return N}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,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 n of t)this.getMaterialMinMaxLODsCount(n,e);return t[i]=e,e}if(M==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const n=t;for(const l of Object.keys(n.uniforms)){const u=n.uniforms[l].value;(u==null?void 0:u.isTexture)===!0&&s(u,e)}}else if(t.isMaterial)for(const n of Object.keys(t)){const l=t[n];(l==null?void 0:l.isTexture)===!0&&s(l,e)}return t[i]=e,e;function s(n,l){const u=r.getAssignedLODInformation(n);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){l.min_count=Math.min(l.min_count,f.lods.length),l.max_count=Math.max(l.max_count,f.lods.length);for(let D=0;D<f.lods.length;D++){const c=f.lods[D];c.width&&(l.lods[D]=l.lods[D]||{min_height:1/0,max_height:0},l.lods[D].min_height=Math.min(l.lods[D].min_height,c.height),l.lods[D].max_height=Math.max(l.lods[D].max_height,c.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 n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,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 h.Mesh||t.isMesh===!0){const i=t.geometry,o=this.getAssignedLODInformation(i);if(!o)return Promise.resolve(null);for(const s of X)(r=s.onBeforeGetLODMesh)==null||r.call(s,t,e);return t["LOD:requested level"]=e,S.getOrLoadLOD(i,e).then(s=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(s)){const n=o.index||0;s=s[n]}s&&i!=s&&((s==null?void 0:s.isBufferGeometry)?(t.geometry=s,M&&Te(t,"geometry",o.url)):M&&console.error("Invalid LOD geometry",s))}return s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else M&&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 h.Material||t.isMaterial===!0){const r=t,i=[],o=new Array;if(M&&De.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const s=r;for(const n of Object.keys(s.uniforms)){const l=s.uniforms[n].value;if((l==null?void 0:l.isTexture)===!0){const u=this.assignTextureLODForSlot(l,e,r,n);i.push(u),o.push(n)}}}else for(const s of Object.keys(r)){const n=r[s];if((n==null?void 0:n.isTexture)===!0){const l=this.assignTextureLODForSlot(n,e,r,s);i.push(l),o.push(s)}}return Promise.all(i).then(s=>{const n=new Array;for(let l=0;l<s.length;l++){const u=s[l],f=o[l];u&&u.isTexture===!0?n.push({material:r,slot:f,texture:u,level:e}):n.push({material:r,slot:f,texture:null,level:e})}return n})}if(t instanceof h.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){if(r&&i){const s=r[i];if(s){const n=this.getAssignedLODInformation(s);if(n&&(n==null?void 0:n.level)<e)return M==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,e,r,s,o),null}r[i]=o}if(M&&i&&r){const s=this.getAssignedLODInformation(t);s&&Te(r,i,s.url)}}return o}else M=="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 M&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((i,o)=>{var s;if(i!=null&&i.extensions){const n=i==null?void 0:i.extensions[N];if(n){if(!n.lods){M&&console.warn("Texture has no LODs",n);return}let l=!1;for(const u of this.parser.associations.keys())u.isTexture===!0&&this.parser.associations.get(u).textures===o&&(l=!0,S.registerTexture(this.url,u,(s=n.lods)==null?void 0:s.length,o,n));l||this.parser.getDependency("texture",o).then(u=>{var f;u&&S.registerTexture(this.url,u,(f=n.lods)==null?void 0:f.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[N];if(s&&s.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const l=this.parser.associations.get(n);l.meshes===o&&S.registerMesh(this.url,s.guid,n,s.lods.length,l.primitives,s)}}}}),null}static async getOrLoadLOD(t,e){var n,l,u,f;const r=M=="verbose",i=t.userData.LODS;if(!i)return null;const o=i==null?void 0:i.key;let s;if(t.isTexture===!0){const D=t;D.source&&D.source[ye]&&(s=D.source[ye])}if(s||(s=S.lodInfos.get(o)),s){if(e>0){let m=!1;const _=Array.isArray(s.lods);if(_&&e>=s.lods.length?m=!0:_||(m=!0),m)return this.lowresCache.get(o)}const D=Array.isArray(s.lods)?(n=s.lods[e])==null?void 0:n.path:s.lods;if(!D)return M&&!s["missing:uri"]&&(s["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,s)),null;const c=We(i.url,D);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!s.guid)return console.warn("missing pointer for glb/gltf texture",s),null;const m=c+"_"+s.guid,_=this.previouslyLoaded.get(m);if(_!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${m}`);let y=await _.catch(Y=>(console.error(`Error loading LOD ${e} from ${c}
|
|
2
|
+
`,Y),null)),w=!1;if(y==null||(y instanceof h.Texture&&t instanceof h.Texture?(l=y.image)!=null&&l.data||(u=y.source)!=null&&u.data?y=this.copySettings(t,y):(w=!0,this.previouslyLoaded.delete(m)):y instanceof h.BufferGeometry&&t instanceof h.BufferGeometry&&((f=y.attributes.position)!=null&&f.array||(w=!0,this.previouslyLoaded.delete(m)))),!w)return y}const L=s,G=new Promise(async(y,w)=>{const Y=new Fe.GLTFLoader;Oe(Y),M&&(await new Promise(T=>setTimeout(T,1e3)),r&&console.warn("Start loading (delayed) "+c,L.guid));let F=c;if(L&&Array.isArray(L.lods)){const T=L.lods[e];T.hash&&(F+="?v="+T.hash)}const O=await Y.loadAsync(F).catch(T=>(console.error(`Error loading LOD ${e} from ${c}
|
|
3
|
+
`,T),null));if(!O)return null;const q=O.parser;r&&console.log("Loading finished "+c,L.guid);let V=0;if(O.parser.json.textures){let T=!1;for(const p of O.parser.json.textures){if(p!=null&&p.extensions){const g=p==null?void 0:p.extensions[N];if(g!=null&&g.guid&&g.guid===L.guid){T=!0;break}}V++}if(T){let p=await q.getDependency("texture",V);return p&&S.assignLODInformation(i.url,p,o,e,void 0,void 0),r&&console.log('change "'+t.name+'" → "'+p.name+'"',c,V,p,m),t instanceof h.Texture&&(p=this.copySettings(t,p)),p&&(p.guid=L.guid),y(p)}else M&&console.warn("Could not find texture with guid",L.guid,O.parser.json)}if(V=0,O.parser.json.meshes){let T=!1;for(const p of O.parser.json.meshes){if(p!=null&&p.extensions){const g=p==null?void 0:p.extensions[N];if(g!=null&&g.guid&&g.guid===L.guid){T=!0;break}}V++}if(T){const p=await q.getDependency("mesh",V),g=L;if(r&&console.log(`Loaded Mesh "${p.name}"`,c,V,p,m),p.isMesh===!0){const v=p.geometry;return S.assignLODInformation(i.url,v,o,e,void 0,g.density),y(v)}else{const v=new Array;for(let A=0;A<p.children.length;A++){const E=p.children[A];if(E.isMesh===!0){const P=E.geometry;S.assignLODInformation(i.url,P,o,e,A,g.density),v.push(P)}}return y(v)}}else M&&console.warn("Could not find mesh with guid",L.guid,O.parser.json)}return y(null)});return this.previouslyLoaded.set(m,G),await G}else if(t instanceof h.Texture){r&&console.log("Load texture from uri: "+c);const _=await new h.TextureLoader().loadAsync(c);return _?(_.guid=s.guid,_.flipY=!1,_.needsUpdate=!0,_.colorSpace=t.colorSpace,r&&console.log(s,_)):M&&console.warn("failed loading",c),_}}else M&&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,s){if(!e)return;e.userData||(e.userData={});const n=new Ye(t,r,i,o,s);e.userData.LODS=n}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=e.clone(),M&&console.warn(`Copying texture settings
|
|
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 b=S;d(b,"registerTexture",(t,e,r,i,o)=>{if(M&&console.log("> Progressive: register texture",i,e.name,e.uuid,e,o),!e){M&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ye]=o);const s=o.guid;S.assignLODInformation(t,e,s,r,i,void 0),S.lodInfos.set(s,o),S.lowresCache.set(s,e)}),d(b,"registerMesh",(t,e,r,i,o,s)=>{var u;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={}),S.assignLODInformation(t,n,e,i,o,s.density),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&&!he(r)&&Ce(r,n);for(const f of X)(u=f.onRegisteredNewMesh)==null||u.call(f,r,s)}),d(b,"lodInfos",new Map),d(b,"previouslyLoaded",new Map),d(b,"lowresCache",new Map);class Ye{constructor(t,e,r,i,o){d(this,"url");d(this,"key");d(this,"level");d(this,"index");d(this,"density");this.url=t,this.key=e,this.level=r,i!=null&&(this.index=i),o!=null&&(this.density=o)}}const k=ie("debugprogressive"),He=ie("noprogressive"),me=Symbol("Needle:LODSManager"),xe=Symbol("Needle:LODState"),H=Symbol("Needle:CurrentLOD"),R={mesh_lod:-1,texture_lod:-1};var B,U,fe,j,J,de,W;const C=class{constructor(t,e){d(this,"context");d(this,"renderer");d(this,"projectionScreenMatrix",new h.Matrix4);d(this,"cameraFrustrum",new h.Frustum);d(this,"targetTriangleDensity",2e5);d(this,"updateInterval","auto");K(this,B,1);d(this,"pause",!1);d(this,"_lodchangedlisteners",[]);K(this,U,void 0);K(this,fe,new h.Clock);K(this,j,0);K(this,J,0);K(this,de,0);K(this,W,0);d(this,"_fpsBuffer",[60,60,60,60,60]);d(this,"_sphere",new h.Sphere);d(this,"_tempBox",new h.Box3);d(this,"_tempBox2",new h.Box3);d(this,"tempMatrix",new h.Matrix4);d(this,"_tempWorldPosition",new h.Vector3);d(this,"_tempBoxSize",new h.Vector3);d(this,"_tempBox2Size",new h.Vector3);this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[xe]}static addPlugin(t){X.push(t)}static removePlugin(t){const e=X.indexOf(t);e>=0&&X.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const r=new C(t,{engine:"unknown",...e});return t[me]=r,r}get plugins(){return X}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}enable(){if(x(this,U))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;$(this,U,this.renderer.render);const e=this;_e(this.renderer),this.renderer.render=function(r,i){e.renderer.getRenderTarget()==null&&(t=0,$(e,j,x(e,j)+1),$(e,J,x(e,fe).getDelta()),$(e,de,x(e,de)+x(e,J)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/x(e,J)),$(e,W,e._fpsBuffer.reduce((l,u)=>l+u)/e._fpsBuffer.length),k&&x(e,j)%30===0&&console.log("FPS",Math.round(x(e,W)),"Interval:",x(e,B)));const s=x(e,j),n=t++;e.onBeforeRender(r,i,n,s),x(e,U).call(this,r,i),e.onAfterRender(r,i,n,s)}}disable(){x(this,U)&&(this.renderer.render=x(this,U),$(this,U,void 0))}onBeforeRender(t,e,r,i){}onAfterRender(t,e,r,i){var l,u;if(this.pause)return;const o=this.renderer.renderLists.get(t,0),s=o.opaque;let n=!0;if(s.length===1){const f=s[0].material;(f.name==="EffectMaterial"||f.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(He||(this.updateInterval==="auto"?x(this,W)<40&&x(this,B)<10?($(this,B,x(this,B)+1),k&&console.warn("↓ Reducing LOD updates",x(this,B),x(this,W).toFixed(0))):x(this,W)>=80&&x(this,B)>1&&($(this,B,x(this,B)-1),k&&console.warn("↑ Increasing LOD updates",x(this,B),x(this,W).toFixed(0))):$(this,B,this.updateInterval),x(this,B)>0&&i%x(this,B)!=0))return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const f=this.targetTriangleDensity;for(const c of s){if(c.material&&(((l=c.geometry)==null?void 0:l.type)==="BoxGeometry"||((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")){k&&(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",c,c.material.name,c.material.type)));continue}switch(c.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&c.material&&!c.object.progressive_debug_color){c.object.progressive_debug_color=!0;const _=Math.random()*16777215,L=new h.MeshStandardMaterial({color:_});c.object.material=L}const m=c.object;(m instanceof h.Mesh||m.isMesh)&&this.updateLODs(t,e,m,f,i)}const D=o.transparent;for(const c of D){const m=c.object;(m instanceof h.Mesh||m.isMesh)&&this.updateLODs(t,e,m,f,i)}}}updateLODs(t,e,r,i,o){var l,u;r.userData||(r.userData={});let s=r[xe];if(s||(s=new je,r[xe]=s),s.frames++<2)return;for(const f of X)(l=f.onBeforeUpdateLOD)==null||l.call(f,this.renderer,t,e,r);this.calculateLodLevel(e,r,s,i,R),R.mesh_lod=Math.round(R.mesh_lod),R.texture_lod=Math.round(R.texture_lod),R.mesh_lod>=0&&this.loadProgressiveMeshes(r,R.mesh_lod);let n=R.texture_lod;if(r.material&&n>=0){const f=r["DEBUG:LOD"];f!=null&&(n=f),this.loadProgressiveTextures(r.material,n)}for(const f of X)(u=f.onAfterUpdatedLOD)==null||u.call(f,this.renderer,t,e,r,R);s.lastLodLevel_Mesh=R.mesh_lod,s.lastLodLevel_Texture=R.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e);return}let r=!1;(t[H]===void 0||e<t[H])&&(r=!0),r&&(t[H]=e,b.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(o=>o({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t[H]!==e){t[H]=e;const r=t.geometry;return b.assignMeshLOD(t,e).then(i=>(i&&t[H]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(o=>o({type:"mesh",level:e,object:t})),i))}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 G,z;if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let n=10+1,l=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=b.getMeshLODInformation(e.geometry),f=u==null?void 0:u.lods,D=f&&f.length>0,c=b.getMaterialMinMaxLODsCount(e.material),m=(c==null?void 0:c.min_count)!=1/0&&c.min_count>0&&c.max_count>0;if(!D&&!m){o.mesh_lod=0,o.texture_lod=0;return}if(D||(l=!0,n=0),!((G=this.cameraFrustrum)!=null&&G.intersectsObject(e))){o.mesh_lod=99,o.texture_lod=99;return}const _=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let L=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const y=e;y.boundingBox||y.computeBoundingBox(),L=y.boundingBox}if(L&&t.isPerspectiveCamera){const y=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(L),this._tempBox.applyMatrix4(e.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 g=this._tempBox.min,v=this._tempBox.max;let A=g.x,E=g.y,P=v.x,Q=v.y;const ne=2,ge=1.5,ae=(g.x+v.x)*.5,le=(g.y+v.y)*.5;A=(A-ae)*ne+ae,E=(E-le)*ne+le,P=(P-ae)*ne+ae,Q=(Q-le)*ne+le;const Ie=A<0&&P>0?0:Math.min(Math.abs(g.x),Math.abs(v.x)),ke=E<0&&Q>0?0:Math.min(Math.abs(g.y),Math.abs(v.y)),pe=Math.max(Ie,ke);r.lastCentrality=(ge-pe)*(ge-pe)*(ge-pe)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&_>0&&w.multiplyScalar(_/screen.availHeight),w.x*=y.aspect;const Y=t.matrixWorldInverse,F=this._tempBox2;F.copy(L),F.applyMatrix4(e.matrixWorld),F.applyMatrix4(Y);const O=F.getSize(this._tempBox2Size),q=Math.max(O.x,O.y);if(Math.max(w.x,w.y)!=0&&q!=0&&(w.z=O.z/Math.max(O.x,O.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,k&&C.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const v=C.corner0,A=C.corner1,E=C.corner2,P=C.corner3;v.copy(this._tempBox.min),A.copy(this._tempBox.max),A.x=v.x,E.copy(this._tempBox.max),E.y=v.y,P.copy(this._tempBox.max);const Q=(v.z+P.z)*.5;v.z=A.z=E.z=P.z=Q,v.applyMatrix4(g),A.applyMatrix4(g),E.applyMatrix4(g),P.applyMatrix4(g),C.debugDrawLine(v,A,255),C.debugDrawLine(v,E,255),C.debugDrawLine(A,P,255),C.debugDrawLine(E,P,255)}let T=999;if(f&&r.lastScreenCoverage>0){for(let g=0;g<f.length;g++)if(f[g].density/r.lastScreenCoverage<i){T=g;break}}T<n&&(n=T,l=!0)}if(l?o.mesh_lod=n:o.mesh_lod=r.lastLodLevel_Mesh,k&&o.mesh_lod!=r.lastLodLevel_Mesh){const w=f==null?void 0:f[o.mesh_lod];w&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${o.mesh_lod} (${w.density.toFixed(0)}) - ${e.name}`)}if(m)if(r.lastLodLevel_Texture<0){if(o.texture_lod=c.max_count-1,k){const y=c.lods[c.max_count-1];k&&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 w=r.lastScreenCoverage*2;((z=this.context)==null?void 0:z.engine)==="model-viewer"&&(w*=2);const F=_/window.devicePixelRatio*w;for(let O=c.lods.length-1;O>=0;O--){let q=c.lods[O];if(!(qe()&&q.max_height>4096)&&q.max_height>F){if(o.texture_lod=O,o.texture_lod<r.lastLodLevel_Texture){const V=q.max_height;k&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${o.texture_lod} = ${V}px
|
|
6
|
+
Screensize: ${F.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}else o.texture_lod=0}};let I=C;B=new WeakMap,U=new WeakMap,fe=new WeakMap,j=new WeakMap,J=new WeakMap,de=new WeakMap,W=new WeakMap,d(I,"debugDrawLine"),d(I,"corner0",new h.Vector3),d(I,"corner1",new h.Vector3),d(I,"corner2",new h.Vector3),d(I,"corner3",new h.Vector3),d(I,"_tempPtInside",new h.Vector3);class je{constructor(){d(this,"frames",0);d(this,"lastLodLevel_Mesh",-1);d(this,"lastLodLevel_Texture",-1);d(this,"lastScreenCoverage",0);d(this,"lastScreenspaceVolume",new h.Vector3);d(this,"lastCentrality",0)}}const Ae=Symbol("NEEDLE_mesh_lod"),ce=Symbol("NEEDLE_texture_lod");let te=null;function Se(){const a=Je();a&&(a.mapURLs(function(t){return Ee(),t}),Ee(),te==null||te.disconnect(),te=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Be(r)})})}),te.observe(document,{childList:!0,subtree:!0}))}function Je(){const a=customElements.get("model-viewer");return a||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Se()}),null)}function Ee(){document.querySelectorAll("model-viewer").forEach(t=>{Be(t)})}const Pe=new WeakSet;let Qe=0;function Be(a){if(!a||Pe.has(a))return null;Pe.add(a),console.debug("[gltf-progressive] found new model-viewer..."+ ++Qe+`
|
|
8
|
+
`,a.getAttribute("src"));let t=null,e=null,r=null;for(let i=a;i!=null;i=Object.getPrototypeOf(i)){const o=Object.getOwnPropertySymbols(i),s=o.find(u=>u.toString()=="Symbol(renderer)"),n=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&s!=null&&(t=a[s].threeRenderer),!e&&n!=null&&(e=a[n]),!r&&l!=null&&(r=a[l])}if(t&&e){let i=function(){if(r){let s=0,n=setInterval(()=>{if(s++>5){clearInterval(n);return}r==null||r.call(a)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=I.get(t,{engine:"model-viewer"});return I.addPlugin(new Ze),o.enable(),o.addEventListener("changed",()=>{r==null||r.call(a)}),a.addEventListener("model-visibility",s=>{s.detail.visible&&(r==null||r.call(a))}),a.addEventListener("load",()=>{i()}),()=>{o.disable()}}return null}class Ze{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[ce]==!0)return;e[ce]=!0;const r=this.tryGetCurrentGLTF(t),i=this.tryGetCurrentModelViewer(t),o=this.getUrl(i);if(o&&r&&e.material){let s=function(l){var f,D,c;if(l[ce]==!0)return;l[ce]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let m=0;m<u.length;m++){const _=u[m],L=l[_];if((L==null?void 0:L.isTexture)===!0){const G=(D=(f=L.userData)==null?void 0:f.associations)==null?void 0:D.textures;if(G==null)continue;const z=r.parser.json.textures[G];if(!z){console.warn("Texture data not found for texture index "+G);continue}if((c=z==null?void 0:z.extensions)!=null&&c[N]){const y=z.extensions[N];y&&o&&b.registerTexture(o,L,y.lods.length,G,y)}}}};const n=e.material;if(Array.isArray(n))for(const l of n)s(l);else s(n)}}tryParseMeshLOD(t,e){var s,n;if(e[Ae]==!0)return;e[Ae]=!0;const r=this.tryGetCurrentModelViewer(t),i=this.getUrl(r);if(!i)return;const o=(n=(s=e.userData)==null?void 0:s.gltfExtensions)==null?void 0:n[N];if(o&&i){const l=e.uuid;b.registerMesh(i,l,e,0,o.lods.length,o)}}}function et(a,t,e,r){_e(t),Oe(e),e.register(o=>new b(o,a));const i=I.get(t);return(r==null?void 0:r.enableLODsManager)!==!1&&i.enable(),i}Se();exports.EXTENSION_NAME=N;exports.LODsManager=I;exports.NEEDLE_progressive=b;exports.VERSION=we;exports.addDracoAndKTX2Loaders=Oe;exports.createLoaders=_e;exports.getRaycastMesh=he;exports.patchModelViewer=Se;exports.setDracoDecoderLocation=ze;exports.setKTX2TranscoderLocation=Ue;exports.setRaycastMesh=Ce;exports.useNeedleProgressive=et;exports.useRaycastMeshes=Ke;
|
package/lib/index.js
CHANGED
|
@@ -4,9 +4,6 @@ export * from "./plugins/index.js";
|
|
|
4
4
|
export { LODsManager } from "./lods_manager.js";
|
|
5
5
|
export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders } from "./loaders.js";
|
|
6
6
|
export * from "./utils.js";
|
|
7
|
-
/** Modelviewer */
|
|
8
|
-
import { patchModelViewer } from "./plugins/modelviewer.js";
|
|
9
|
-
patchModelViewer();
|
|
10
7
|
import { addDracoAndKTX2Loaders, createLoaders } from "./loaders.js";
|
|
11
8
|
import { NEEDLE_progressive } from "./extension.js";
|
|
12
9
|
import { LODsManager } from "./lods_manager.js";
|
|
@@ -36,3 +33,6 @@ export function useNeedleProgressive(url, renderer, loader, opts) {
|
|
|
36
33
|
}
|
|
37
34
|
return lod;
|
|
38
35
|
}
|
|
36
|
+
/** Modelviewer */
|
|
37
|
+
import { patchModelViewer } from "./plugins/modelviewer.js";
|
|
38
|
+
patchModelViewer();
|
package/lib/lods_manager.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ declare type LODChangedEventListener = (args: {
|
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
|
44
44
|
export declare class LODsManager {
|
|
45
|
+
#private;
|
|
45
46
|
/** 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.
|
|
46
47
|
*/
|
|
47
48
|
static debugDrawLine?: (a: Vector3, b: Vector3, color: number) => void;
|
|
@@ -67,9 +68,9 @@ export declare class LODsManager {
|
|
|
67
68
|
*/
|
|
68
69
|
targetTriangleDensity: number;
|
|
69
70
|
/**
|
|
70
|
-
* The update interval in frames. If set to 0, the LODs will be updated every frame. If set to
|
|
71
|
+
* The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 2, the LODs will be updated every second frame, etc.
|
|
71
72
|
*/
|
|
72
|
-
updateInterval: number;
|
|
73
|
+
updateInterval: "auto" | number;
|
|
73
74
|
/**
|
|
74
75
|
* If set to true, the LODsManager will not update the LODs.
|
|
75
76
|
*/
|
|
@@ -77,8 +78,7 @@ export declare class LODsManager {
|
|
|
77
78
|
private readonly _lodchangedlisteners;
|
|
78
79
|
addEventListener(evt: "changed", listener: LODChangedEventListener): void;
|
|
79
80
|
private constructor();
|
|
80
|
-
private
|
|
81
|
-
private _originalRender?;
|
|
81
|
+
private _fpsBuffer;
|
|
82
82
|
/**
|
|
83
83
|
* Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
|
|
84
84
|
*/
|
package/lib/lods_manager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box3, Frustum, Matrix4, Mesh, Sphere, Vector3 } from "three";
|
|
1
|
+
import { Box3, Clock, Frustum, Matrix4, Mesh, MeshStandardMaterial, Sphere, Vector3 } from "three";
|
|
2
2
|
import { NEEDLE_progressive } from "./extension.js";
|
|
3
3
|
import { createLoaders } from "./loaders.js";
|
|
4
4
|
import { getParam, isMobileDevice } from "./utils.internal.js";
|
|
@@ -83,9 +83,10 @@ export class LODsManager {
|
|
|
83
83
|
*/
|
|
84
84
|
targetTriangleDensity = 200_000;
|
|
85
85
|
/**
|
|
86
|
-
* The update interval in frames. If set to 0, the LODs will be updated every frame. If set to
|
|
86
|
+
* The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 2, the LODs will be updated every second frame, etc.
|
|
87
87
|
*/
|
|
88
|
-
updateInterval =
|
|
88
|
+
updateInterval = "auto";
|
|
89
|
+
#updateInterval = 1;
|
|
89
90
|
/**
|
|
90
91
|
* If set to true, the LODsManager will not update the LODs.
|
|
91
92
|
*/
|
|
@@ -101,18 +102,23 @@ export class LODsManager {
|
|
|
101
102
|
this.renderer = renderer;
|
|
102
103
|
this.context = { ...context };
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
#originalRender;
|
|
106
|
+
#clock = new Clock();
|
|
107
|
+
#frame = 0;
|
|
108
|
+
#delta = 0;
|
|
109
|
+
#time = 0;
|
|
110
|
+
#fps = 0;
|
|
111
|
+
_fpsBuffer = [60, 60, 60, 60, 60];
|
|
106
112
|
/**
|
|
107
113
|
* Enable the LODsManager. This will replace the render method of the renderer with a method that updates the LODs.
|
|
108
114
|
*/
|
|
109
115
|
enable() {
|
|
110
|
-
if (this
|
|
116
|
+
if (this.#originalRender)
|
|
111
117
|
return;
|
|
112
118
|
console.debug("[gltf-progressive] Enabling LODsManager for renderer");
|
|
113
119
|
let stack = 0;
|
|
114
120
|
// Save the original render method
|
|
115
|
-
this
|
|
121
|
+
this.#originalRender = this.renderer.render;
|
|
116
122
|
const self = this;
|
|
117
123
|
createLoaders(this.renderer);
|
|
118
124
|
this.renderer.render = function (scene, camera) {
|
|
@@ -122,20 +128,27 @@ export class LODsManager {
|
|
|
122
128
|
const renderTarget = self.renderer.getRenderTarget();
|
|
123
129
|
if (renderTarget == null) {
|
|
124
130
|
stack = 0;
|
|
125
|
-
self
|
|
131
|
+
self.#frame += 1;
|
|
132
|
+
self.#delta = self.#clock.getDelta();
|
|
133
|
+
self.#time += self.#delta;
|
|
134
|
+
self._fpsBuffer.shift();
|
|
135
|
+
self._fpsBuffer.push(1 / self.#delta);
|
|
136
|
+
self.#fps = self._fpsBuffer.reduce((a, b) => a + b) / self._fpsBuffer.length;
|
|
137
|
+
if (debugProgressiveLoading && self.#frame % 30 === 0)
|
|
138
|
+
console.log("FPS", Math.round(self.#fps), "Interval:", self.#updateInterval);
|
|
126
139
|
}
|
|
127
|
-
const frame = self
|
|
140
|
+
const frame = self.#frame;
|
|
128
141
|
const stack_level = stack++;
|
|
129
142
|
self.onBeforeRender(scene, camera, stack_level, frame);
|
|
130
|
-
self.
|
|
143
|
+
self.#originalRender.call(this, scene, camera);
|
|
131
144
|
self.onAfterRender(scene, camera, stack_level, frame);
|
|
132
145
|
};
|
|
133
146
|
}
|
|
134
147
|
disable() {
|
|
135
|
-
if (!this
|
|
148
|
+
if (!this.#originalRender)
|
|
136
149
|
return;
|
|
137
|
-
this.renderer.render = this
|
|
138
|
-
this
|
|
150
|
+
this.renderer.render = this.#originalRender;
|
|
151
|
+
this.#originalRender = undefined;
|
|
139
152
|
}
|
|
140
153
|
onBeforeRender(_scene, _camera, _stack, _frame) {
|
|
141
154
|
}
|
|
@@ -170,8 +183,27 @@ export class LODsManager {
|
|
|
170
183
|
if (updateLODs) {
|
|
171
184
|
if (suppressProgressiveLoading)
|
|
172
185
|
return;
|
|
173
|
-
|
|
186
|
+
// If the update interval is set to auto then we check the FPS and adjust the update interval accordingly
|
|
187
|
+
// If performance is low we increase the update interval to reduce the amount of LOD updates
|
|
188
|
+
if (this.updateInterval === "auto") {
|
|
189
|
+
if (this.#fps < 40 && this.#updateInterval < 10) {
|
|
190
|
+
this.#updateInterval += 1;
|
|
191
|
+
if (debugProgressiveLoading)
|
|
192
|
+
console.warn("↓ Reducing LOD updates", this.#updateInterval, this.#fps.toFixed(0));
|
|
193
|
+
}
|
|
194
|
+
else if (this.#fps >= 80 && this.#updateInterval > 1) {
|
|
195
|
+
this.#updateInterval -= 1;
|
|
196
|
+
if (debugProgressiveLoading)
|
|
197
|
+
console.warn("↑ Increasing LOD updates", this.#updateInterval, this.#fps.toFixed(0));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this.#updateInterval = this.updateInterval;
|
|
202
|
+
}
|
|
203
|
+
// Check if we should update LODs this frame
|
|
204
|
+
if (this.#updateInterval > 0 && frame % this.#updateInterval != 0) {
|
|
174
205
|
return;
|
|
206
|
+
}
|
|
175
207
|
this.projectionScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
176
208
|
this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
|
|
177
209
|
const desiredDensity = this.targetTriangleDensity;
|
|
@@ -207,6 +239,16 @@ export class LODsManager {
|
|
|
207
239
|
case "MeshDepthMaterial":
|
|
208
240
|
continue;
|
|
209
241
|
}
|
|
242
|
+
if (debugProgressiveLoading === "color") {
|
|
243
|
+
if (entry.material) {
|
|
244
|
+
if (!entry.object["progressive_debug_color"]) {
|
|
245
|
+
entry.object["progressive_debug_color"] = true;
|
|
246
|
+
const randomColor = Math.random() * 0xffffff;
|
|
247
|
+
const newMaterial = new MeshStandardMaterial({ color: randomColor });
|
|
248
|
+
entry.object.material = newMaterial;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
210
252
|
const object = entry.object;
|
|
211
253
|
if (object instanceof Mesh || (object.isMesh)) {
|
|
212
254
|
this.updateLODs(scene, camera, object, desiredDensity, frame);
|
|
@@ -2,29 +2,64 @@ import { LODsManager } from "../lods_manager.js";
|
|
|
2
2
|
import { EXTENSION_NAME, NEEDLE_progressive } from "../extension.js";
|
|
3
3
|
const $meshLODSymbol = Symbol("NEEDLE_mesh_lod");
|
|
4
4
|
const $textureLODSymbol = Symbol("NEEDLE_texture_lod");
|
|
5
|
+
let documentObserver = null;
|
|
5
6
|
export function patchModelViewer() {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const ModelViewerElement = tryGetModelViewerConstructor();
|
|
8
|
+
if (!ModelViewerElement) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
ModelViewerElement.mapURLs(function (url) {
|
|
12
|
+
searchModelViewers();
|
|
13
|
+
return url;
|
|
14
|
+
});
|
|
8
15
|
searchModelViewers();
|
|
16
|
+
// observe the document for new model-viewers
|
|
17
|
+
documentObserver?.disconnect();
|
|
18
|
+
documentObserver = new MutationObserver((mutations) => {
|
|
19
|
+
mutations.forEach((mutation) => {
|
|
20
|
+
mutation.addedNodes.forEach((node) => {
|
|
21
|
+
if (node instanceof HTMLElement && node.tagName.toLowerCase() === "model-viewer") {
|
|
22
|
+
_patchModelViewer(node);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
documentObserver.observe(document, { childList: true, subtree: true });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Tries to get the mode-viewer constructor from the custom element registry. If it doesnt exist yet we will wait for it to be loaded in case it's added to the document at a later point
|
|
31
|
+
*/
|
|
32
|
+
function tryGetModelViewerConstructor() {
|
|
33
|
+
// If model-viewer is already registered we can ignore this
|
|
34
|
+
const ModelViewerElement = customElements.get('model-viewer');
|
|
35
|
+
if (ModelViewerElement)
|
|
36
|
+
return ModelViewerElement;
|
|
37
|
+
// wait for model-viewer to be defined
|
|
38
|
+
customElements.whenDefined('model-viewer').then(() => {
|
|
39
|
+
console.debug("[gltf-progressive] model-viewer defined");
|
|
40
|
+
patchModelViewer();
|
|
41
|
+
});
|
|
42
|
+
return null;
|
|
9
43
|
}
|
|
10
44
|
function searchModelViewers() {
|
|
11
45
|
// Query once for model viewer. If a user does not have model-viewer in their page, this will return null.
|
|
12
46
|
const modelviewers = document.querySelectorAll("model-viewer");
|
|
13
|
-
modelviewers.forEach((modelviewer
|
|
14
|
-
_patchModelViewer(modelviewer
|
|
47
|
+
modelviewers.forEach((modelviewer) => {
|
|
48
|
+
_patchModelViewer(modelviewer);
|
|
15
49
|
});
|
|
16
50
|
}
|
|
17
51
|
const foundModelViewers = new WeakSet();
|
|
52
|
+
let modelViewerCount = 0;
|
|
18
53
|
/** Patch modelviewer to support NEEDLE progressive system
|
|
19
54
|
* @returns a function to remove the patch
|
|
20
55
|
*/
|
|
21
|
-
function _patchModelViewer(modelviewer
|
|
56
|
+
function _patchModelViewer(modelviewer) {
|
|
22
57
|
if (!modelviewer)
|
|
23
58
|
return null;
|
|
24
59
|
if (foundModelViewers.has(modelviewer))
|
|
25
60
|
return null;
|
|
26
61
|
foundModelViewers.add(modelviewer);
|
|
27
|
-
console.debug("[gltf-progressive] found model-viewer..." +
|
|
62
|
+
console.debug("[gltf-progressive] found new model-viewer..." + (++modelViewerCount) + "\n", modelviewer.getAttribute("src"));
|
|
28
63
|
// Find the necessary internal methods and properties. We need access to the scene, renderer
|
|
29
64
|
let renderer = null;
|
|
30
65
|
let scene = null;
|
package/lib/version.js
CHANGED
package/package.json
CHANGED