@needle-tools/gltf-progressive 3.0.1 → 3.1.0
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 +4 -0
- package/gltf-progressive.js +520 -476
- package/gltf-progressive.min.js +7 -7
- package/gltf-progressive.umd.cjs +7 -7
- package/lib/extension.d.ts +1 -0
- package/lib/extension.js +30 -21
- package/lib/lods.manager.d.ts +2 -0
- package/lib/lods.manager.js +20 -9
- package/lib/utils.internal.d.ts +20 -0
- package/lib/utils.internal.js +49 -0
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var Ye=Object.defineProperty,Je=(t,e,s)=>e in t?Ye(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s,u=(t,e,s)=>(Je(t,typeof e!="symbol"?e+"":e,s),s),Ee=(t,e,s)=>{if(!e.has(t))throw TypeError("Cannot "+s)},v=(t,e,s)=>(Ee(t,e,"read from private field"),s?s.call(t):e.get(t)),Y=(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)},U=(t,e,s,o)=>(Ee(t,e,"write to private field"),o?o.call(t,s):e.set(t,s),s);import{BufferGeometry as ue,Mesh as J,Texture as se,TextureLoader as Qe,Matrix4 as Ce,Clock as Ze,MeshStandardMaterial as et,Sphere as tt,Box3 as Ie,Vector3 as z}from"three";import{GLTFLoader as ye}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as st}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as rt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as ot}from"three/examples/jsm/loaders/KTX2Loader.js";const ke="";globalThis.GLTF_PROGRESSIVE_VERSION=ke,console.debug("[gltf-progressive] version -");let $="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",Z="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const it=$,nt=Z,Be=new URL($+"draco_decoder.js");Be.searchParams.append("range","true"),fetch(Be,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${$} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),$===it&&Ge("./include/draco/"),Z===nt&&Re("./include/ktx2/")}).finally(()=>{je()});function Ge(t){$=t,k&&k[Le]!=$?(console.debug("Updating Draco decoder path to "+t),k[Le]=$,k.setDecoderPath($),k.preload()):console.debug("Setting Draco decoder path to "+t)}function Re(t){Z=t,j&&j.transcoderPath!=Z?(console.debug("Updating KTX2 transcoder path to "+t),j.setTranscoderPath(Z),j.init()):console.debug("Setting KTX2 transcoder path to "+t)}const Le=Symbol("dracoDecoderPath");let k,de,j;function je(){k||(k=new rt,k[Le]=$,k.setDecoderPath($),k.setDecoderConfig({type:"js"}),k.preload()),j||(j=new ot,j.setTranscoderPath(Z),j.init()),de||(de=st)}function _e(t){return je(),t?j.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:k,ktx2Loader:j,meshoptDecoder:de}}function we(t){t.dracoLoader||t.setDRACOLoader(k),t.ktx2Loader||t.setKTX2Loader(j),t.meshoptDecoder||t.setMeshoptDecoder(de)}const Me=new WeakMap;function De(t,e){let s=Me.get(t);s?s=Object.assign(s,e):s=e,Me.set(t,s)}const Ne=ye.prototype.load;function at(...t){const e=Me.get(this);let s=t[0];const o=new URL(s,window.location.href);if(o.hostname.endsWith("needle.tools")){const r=e?.progressive!==void 0?e.progressive:!0,i=e!=null&&e.usecase?e.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,s=o.toString()}return t[0]=s,Ne?.call(this,...t)}ye.prototype.load=at,re("debugprogressive");function re(t){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function lt(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 ce;function ut(){return ce!==void 0||(ce=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),re("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ce)),ce}function dt(){if(typeof window>"u")return!1;const t=new URL(window.location.href),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}const ct=typeof window>"u"&&typeof document>"u",be=Symbol("needle:raycast-mesh");function ee(t){return t?.[be]instanceof ue?t[be]:null}function $e(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!ee(t)){const s=ht(e);s.userData={isRaycastMesh:!0},t[be]=s}}function We(t=!0){if(t){if(oe)return;const e=oe=J.prototype.raycast;J.prototype.raycast=function(s,o){const r=this,i=ee(r);let n;i&&r.isMesh&&(n=r.geometry,r.geometry=i),e.call(this,s,o),n&&(r.geometry=n)}}else{if(!oe)return;J.prototype.raycast=oe,oe=null}}let oe=null;function ht(t){const e=new ue;for(const s in t.attributes)e.setAttribute(s,t.getAttribute(s));return e.setIndex(t.getIndex()),e}const V=new Array,x=re("debugprogressive");let he,Q=-1;if(x){let t=function(){Q+=1,Q>=e&&(Q=-1),console.log(`Toggle LOD level [${Q}]`)},e=6;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(he=!he,console.log(`Toggle wireframe [${he}]`));const o=parseInt(s.key);!isNaN(o)&&o>=0&&(Q=o,console.log(`Set LOD level to [${Q}]`))})}function Fe(t){if(x)if(Array.isArray(t))for(const e of t)Fe(e);else t&&"wireframe"in t&&(t.wireframe=he===!0)}const W="NEEDLE_progressive",Oe=Symbol("needle-progressive-texture"),M=class{constructor(t,e){u(this,"parser"),u(this,"url"),u(this,"_isLoadingMesh"),u(this,"loadMesh",s=>{var o,r;if(this._isLoadingMesh)return null;const i=(r=(o=this.parser.json.meshes[s])==null?void 0:o.extensions)==null?void 0:r[W];return i?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",s).then(n=>{var l;return this._isLoadingMesh=!1,n&&M.registerMesh(this.url,i.guid,n,(l=i.lods)==null?void 0:l.length,0,i),n})):null}),x&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return W}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var e;return((e=this.getAssignedLODInformation(t))==null?void 0:e.index)??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,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 n of t)this.getMaterialMinMaxLODsCount(n,e);return t[o]=e,e}if(x==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const n=t;for(const l of Object.keys(n.uniforms)){const d=n.uniforms[l].value;d?.isTexture===!0&&i(d,e)}}else if(t.isMaterial)for(const n of Object.keys(t)){const l=t[n];l?.isTexture===!0&&i(l,e)}return t[o]=e,e;function i(n,l){const d=s.getAssignedLODInformation(n);if(d){const a=s.lodInfos.get(d.key);if(a&&a.lods){l.min_count=Math.min(l.min_count,a.lods.length),l.max_count=Math.max(l.max_count,a.lods.length);for(let h=0;h<a.lods.length;h++){const y=a.lods[h];y.width&&(l.lods[h]=l.lods[h]||{min_height:1/0,max_height:0},l.lods[h].min_height=Math.min(l.lods[h].min_height,y.height),l.lods[h].max_height=Math.max(l.lods[h].max_height,y.height))}}}}}static hasLODLevelAvailable(t,e){var s;if(Array.isArray(t)){for(const i of t)if(this.hasLODLevelAvailable(i,e))return!0;return!1}if(t.isMaterial===!0){for(const i of Object.keys(t)){const n=t[i];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const i of t.children)if(i.isMesh===!0&&this.hasLODLevelAvailable(i,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 i=o.userData.LODS;if(r=this.lodInfos.get(i.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 J||t.isMesh===!0){const o=t.geometry,r=this.getAssignedLODInformation(o);if(!r)return Promise.resolve(null);for(const i of V)(s=i.onBeforeGetLODMesh)==null||s.call(i,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(o,e).then(i=>{if(Array.isArray(i)){const n=r.index||0;i=i[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],i&&o!=i&&(i?.isBufferGeometry?t.geometry=i:x&&console.error("Invalid LOD geometry",i))),i}).catch(i=>(console.error("Error loading mesh LOD",t,i),null))}else x&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const i=this.assignTextureLOD(r,e);o.push(i)}return Promise.all(o).then(r=>{const i=new Array;for(const n of r)Array.isArray(n)&&i.push(...n);return i})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const i=s;for(const n of Object.keys(i.uniforms)){const l=i.uniforms[n].value;if(l?.isTexture===!0){const d=this.assignTextureLODForSlot(l,e,s,n).then(a=>(a&&i.uniforms[n].value!=a&&(i.uniforms[n].value=a,i.uniformsNeedUpdate=!0),a));o.push(d),r.push(n)}}}else for(const i of Object.keys(s)){const n=s[i];if(n?.isTexture===!0){const l=this.assignTextureLODForSlot(n,e,s,i);o.push(l),r.push(i)}}return Promise.all(o).then(i=>{const n=new Array;for(let l=0;l<i.length;l++){const d=i[l],a=r[l];d&&d.isTexture===!0?n.push({material:s,slot:a,texture:d,level:e}):n.push({material:s,slot:a,texture:null,level:e})}return n})}if(t instanceof se||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):M.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if(r?.isTexture===!0){if(r!=t&&s&&o){const i=s[o];if(i&&!x){const n=this.getAssignedLODInformation(i);if(n&&n?.level<e)return x==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,e,s,i,r),null}s[o]=r}return r}else x=="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 x&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((o,r)=>{var i;if(o!=null&&o.extensions){const n=o?.extensions[W];if(n){if(!n.lods){x&&console.warn("Texture has no LODs",n);return}let l=!1;for(const d of this.parser.associations.keys())if(d.isTexture===!0){const a=this.parser.associations.get(d);a?.textures===r&&(l=!0,M.registerTexture(this.url,d,(i=n.lods)==null?void 0:i.length,r,n))}l||this.parser.getDependency("texture",r).then(d=>{var a;d&&M.registerTexture(this.url,d,(a=n.lods)==null?void 0:a.length,r,n)})}}}),(s=this.parser.json.meshes)==null||s.forEach((o,r)=>{if(o!=null&&o.extensions){const i=o?.extensions[W];if(i&&i.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const l=this.parser.associations.get(n);l?.meshes===r&&M.registerMesh(this.url,i.guid,n,i.lods.length,l.primitives,i)}}}}),null}static async getOrLoadLOD(t,e){var s,o,r,i;const n=x=="verbose",l=t.userData.LODS;if(!l)return null;const d=l?.key;let a;if(t.isTexture===!0){const h=t;h.source&&h.source[Oe]&&(a=h.source[Oe])}if(a||(a=M.lodInfos.get(d)),a){if(e>0){let m=!1;const w=Array.isArray(a.lods);if(w&&e>=a.lods.length?m=!0:w||(m=!0),m)return this.lowresCache.get(d)}const h=Array.isArray(a.lods)?(s=a.lods[e])==null?void 0:s.path:a.lods;if(!h)return x&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const y=lt(l.url,h);if(y.endsWith(".glb")||y.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const m=y+"_"+a.guid,w=this.previouslyLoaded.get(m);if(w!==void 0){n&&console.log(`LOD ${e} was already loading/loaded: ${m}`);let L=await w.catch(f=>(console.error(`Error loading LOD ${e} from ${y}
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
1
|
+
var Qe=Object.defineProperty,Ye=(t,e,s)=>e in t?Qe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s,u=(t,e,s)=>(Ye(t,typeof e!="symbol"?e+"":e,s),s),Ee=(t,e,s)=>{if(!e.has(t))throw TypeError("Cannot "+s)},y=(t,e,s)=>(Ee(t,e,"read from private field"),s?s.call(t):e.get(t)),Y=(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)},U=(t,e,s,n)=>(Ee(t,e,"write to private field"),n?n.call(t,s):e.set(t,s),s);import{BufferGeometry as ue,Mesh as Z,Texture as se,TextureLoader as Ze,Matrix4 as Ce,Clock as Je,MeshStandardMaterial as et,Sphere as tt,Box3 as ke,Vector3 as W}from"three";import{GLTFLoader as xe}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as st}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as rt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as nt}from"three/examples/jsm/loaders/KTX2Loader.js";const Be="";globalThis.GLTF_PROGRESSIVE_VERSION=Be,console.debug("[gltf-progressive] version -");let N="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",ee="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const ot=N,it=ee,Ie=new URL(N+"draco_decoder.js");Ie.searchParams.append("range","true"),fetch(Ie,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${N} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),N===ot&&Ge("./include/draco/"),ee===it&&Re("./include/ktx2/")}).finally(()=>{$e()});function Ge(t){N=t,I&&I[_e]!=N?(console.debug("Updating Draco decoder path to "+t),I[_e]=N,I.setDecoderPath(N),I.preload()):console.debug("Setting Draco decoder path to "+t)}function Re(t){ee=t,R&&R.transcoderPath!=ee?(console.debug("Updating KTX2 transcoder path to "+t),R.setTranscoderPath(ee),R.init()):console.debug("Setting KTX2 transcoder path to "+t)}const _e=Symbol("dracoDecoderPath");let I,de,R;function $e(){I||(I=new rt,I[_e]=N,I.setDecoderPath(N),I.setDecoderConfig({type:"js"}),I.preload()),R||(R=new nt,R.setTranscoderPath(ee),R.init()),de||(de=st)}function Le(t){return $e(),t?R.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:I,ktx2Loader:R,meshoptDecoder:de}}function we(t){t.dracoLoader||t.setDRACOLoader(I),t.ktx2Loader||t.setKTX2Loader(R),t.meshoptDecoder||t.setMeshoptDecoder(de)}const Me=new WeakMap;function be(t,e){let s=Me.get(t);s?s=Object.assign(s,e):s=e,Me.set(t,s)}const je=xe.prototype.load;function at(...t){const e=Me.get(this);let s=t[0];const n=new URL(s,window.location.href);if(n.hostname.endsWith("needle.tools")){const r=e?.progressive!==void 0?e.progressive:!0,o=e!=null&&e.usecase?e.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,s=n.toString()}return t[0]=s,je?.call(this,...t)}xe.prototype.load=at,re("debugprogressive");function re(t){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function lt(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const s=t.lastIndexOf("/");if(s>=0){const n=t.substring(0,s+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}let ce;function ut(){return ce!==void 0||(ce=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),re("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ce)),ce}function dt(){if(typeof window>"u")return!1;const t=new URL(window.location.href),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class ct{constructor(e=100,s={}){u(this,"_running",new Map),u(this,"_queue",[]),u(this,"debug",!1),u(this,"tick",()=>{this.internalUpdate(),setTimeout(this.tick,10)}),this.maxConcurrent=e,this.debug=s.debug??!1,window.requestAnimationFrame(this.tick)}slot(e){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${e}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(s=>{this._queue.push({key:e,resolve:s})})}add(e,s){this._running.has(e)||(this._running.set(e,s),s.finally(()=>{this._running.delete(e),this.debug&&console.debug(`[PromiseQueue]: Promise for key ${e} finished, running: ${this._running.size}, waiting: ${this._queue.length}`)}),this.debug&&console.debug(`[PromiseQueue]: Adding promise for key ${e}, running: ${this._running.size}, waiting: ${this._queue.length}`))}internalUpdate(){const e=this.maxConcurrent-this._running.size;for(let s=0;s<e&&this._queue.length>0;s++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:n,resolve:r}=this._queue.shift();r({use:o=>this.add(n,o)})}}}const ht=typeof window>"u"&&typeof document>"u",De=Symbol("needle:raycast-mesh");function te(t){return t?.[De]instanceof ue?t[De]:null}function Ne(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!te(t)){const s=gt(e);s.userData={isRaycastMesh:!0},t[De]=s}}function Fe(t=!0){if(t){if(ne)return;const e=ne=Z.prototype.raycast;Z.prototype.raycast=function(s,n){const r=this,o=te(r);let i;o&&r.isMesh&&(i=r.geometry,r.geometry=o),e.call(this,s,n),i&&(r.geometry=i)}}else{if(!ne)return;Z.prototype.raycast=ne,ne=null}}let ne=null;function gt(t){const e=new ue;for(const s in t.attributes)e.setAttribute(s,t.getAttribute(s));return e.setIndex(t.getIndex()),e}const q=new Array,m=re("debugprogressive");let he,J=-1;if(m){let t=function(){J+=1,J>=e&&(J=-1),console.log(`Toggle LOD level [${J}]`)},e=6;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(he=!he,console.log(`Toggle wireframe [${he}]`));const n=parseInt(s.key);!isNaN(n)&&n>=0&&(J=n,console.log(`Set LOD level to [${J}]`))})}function Ue(t){if(m)if(Array.isArray(t))for(const e of t)Ue(e);else t&&"wireframe"in t&&(t.wireframe=he===!0)}const F="NEEDLE_progressive",Oe=Symbol("needle-progressive-texture"),M=class{constructor(t,e){u(this,"parser"),u(this,"url"),u(this,"_isLoadingMesh"),u(this,"loadMesh",s=>{var n,r;if(this._isLoadingMesh)return null;const o=(r=(n=this.parser.json.meshes[s])==null?void 0:n.extensions)==null?void 0:r[F];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",s).then(i=>{var l;return this._isLoadingMesh=!1,i&&M.registerMesh(this.url,o.guid,i,(l=o.lods)==null?void 0:l.length,0,o),i})):null}),m&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var e;return((e=this.getAssignedLODInformation(t))==null?void 0:e.index)??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,n="LODS:minmax",r=t[n];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const i of t)this.getMaterialMinMaxLODsCount(i,e);return t[n]=e,e}if(m==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const l of Object.keys(i.uniforms)){const d=i.uniforms[l].value;d?.isTexture===!0&&o(d,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const l=t[i];l?.isTexture===!0&&o(l,e)}return t[n]=e,e;function o(i,l){const d=s.getAssignedLODInformation(i);if(d){const a=s.lodInfos.get(d.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 g=0;g<a.lods.length;g++){const x=a.lods[g];x.width&&(l.lods[g]=l.lods[g]||{min_height:1/0,max_height:0},l.lods[g].min_height=Math.min(l.lods[g].min_height,x.height),l.lods[g].max_height=Math.max(l.lods[g].max_height,x.height))}}}}}static hasLODLevelAvailable(t,e){var s;if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let n,r;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(s=n?.userData)!=null&&s.LODS){const o=n.userData.LODS;if(r=this.lodInfos.get(o.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e){var s;if(!t)return Promise.resolve(null);if(t instanceof Z||t.isMesh===!0){const n=t.geometry,r=this.getAssignedLODInformation(n);if(!r)return Promise.resolve(null);for(const o of q)(s=o.onBeforeGetLODMesh)==null||s.call(o,t,e);return t["LOD:requested level"]=e,M.getOrLoadLOD(n,e).then(o=>{if(Array.isArray(o)){const i=r.index||0;o=o[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&n!=o&&(o?.isBufferGeometry?t.geometry=o:m&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),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.isMesh===!0){const s=t;if(Array.isArray(s.material)){const n=new Array;for(const r of s.material){const o=this.assignTextureLOD(r,e);n.push(o)}return Promise.all(n).then(r=>{const o=new Array;for(const i of r)Array.isArray(i)&&o.push(...i);return o})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,n=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const o=s;for(const i of Object.keys(o.uniforms)){const l=o.uniforms[i].value;if(l?.isTexture===!0){const d=this.assignTextureLODForSlot(l,e,s,i).then(a=>(a&&o.uniforms[i].value!=a&&(o.uniforms[i].value=a,o.uniformsNeedUpdate=!0),a));n.push(d),r.push(i)}}}else for(const o of Object.keys(s)){const i=s[o];if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,s,o);n.push(l),r.push(o)}}return Promise.all(n).then(o=>{const i=new Array;for(let l=0;l<o.length;l++){const d=o[l],a=r[l];d&&d.isTexture===!0?i.push({material:s,slot:a,texture:d,level:e}):i.push({material:s,slot:a,texture:null,level:e})}return i})}if(t instanceof se||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):M.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if(r?.isTexture===!0){if(r!=t&&s&&n){const o=s[n];if(o&&!m){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return m==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,s,o,r),null}s[n]=r}return r}else m=="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 m&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,r)=>{var o;if(n!=null&&n.extensions){const i=n?.extensions[F];if(i){if(!i.lods){m&&console.warn("Texture has no LODs",i);return}let l=!1;for(const d of this.parser.associations.keys())if(d.isTexture===!0){const a=this.parser.associations.get(d);a?.textures===r&&(l=!0,M.registerTexture(this.url,d,(o=i.lods)==null?void 0:o.length,r,i))}l||this.parser.getDependency("texture",r).then(d=>{var a;d&&M.registerTexture(this.url,d,(a=i.lods)==null?void 0:a.length,r,i)})}}}),(s=this.parser.json.meshes)==null||s.forEach((n,r)=>{if(n!=null&&n.extensions){const o=n?.extensions[F];if(o&&o.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const l=this.parser.associations.get(i);l?.meshes===r&&M.registerMesh(this.url,o.guid,i,o.lods.length,l.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var s,n,r,o;const i=m=="verbose",l=t.userData.LODS;if(!l)return null;const d=l?.key;let a;if(t.isTexture===!0){const g=t;g.source&&g.source[Oe]&&(a=g.source[Oe])}if(a||(a=M.lodInfos.get(d)),a){if(e>0){let p=!1;const L=Array.isArray(a.lods);if(L&&e>=a.lods.length?p=!0:L||(p=!0),p)return this.lowresCache.get(d)}const g=Array.isArray(a.lods)?(s=a.lods[e])==null?void 0:s.path:a.lods;if(!g)return m&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const x=lt(l.url,g);if(x.endsWith(".glb")||x.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const p=x+"_"+a.guid,L=this.previouslyLoaded.get(p);if(L!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${p}`);let h=await L.catch(T=>(console.error(`Error loading LOD ${e} from ${x}
|
|
2
|
+
`,T),null)),v=!1;if(h==null||(h instanceof se&&t instanceof se?(n=h.image)!=null&&n.data||(r=h.source)!=null&&r.data?h=this.copySettings(t,h):(v=!0,this.previouslyLoaded.delete(p)):h instanceof ue&&t instanceof ue&&((o=h.attributes.position)!=null&&o.array||(v=!0,this.previouslyLoaded.delete(p)))),!v)return h}const Q=await this.queue.slot(x);if(!Q.use)return m&&console.log(`LOD ${e} was aborted: ${x}`),null;const w=a,k=new Promise(async(h,v)=>{const T=new xe;we(T),m&&(await new Promise(f=>setTimeout(f,1e3)),i&&console.warn("Start loading (delayed) "+x,w.guid));let G=x;if(w&&Array.isArray(w.lods)){const f=w.lods[e];f.hash&&(G+="?v="+f.hash)}const D=await T.loadAsync(G).catch(f=>(console.error(`Error loading LOD ${e} from ${x}
|
|
3
|
+
`,f),h(null)));if(!D)return h(null);const j=D.parser;i&&console.log("Loading finished "+x,w.guid);let O=0;if(D.parser.json.textures){let f=!1;for(const c of D.parser.json.textures){if(c!=null&&c.extensions){const _=c?.extensions[F];if(_!=null&&_.guid&&_.guid===w.guid){f=!0;break}}O++}if(f){let c=await j.getDependency("texture",O);return c&&M.assignLODInformation(l.url,c,d,e,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+c.name+'"',x,O,c,p),t instanceof se&&(c=this.copySettings(t,c)),c&&(c.guid=w.guid),h(c)}else m&&console.warn("Could not find texture with guid",w.guid,D.parser.json)}if(O=0,D.parser.json.meshes){let f=!1;for(const c of D.parser.json.meshes){if(c!=null&&c.extensions){const _=c?.extensions[F];if(_!=null&&_.guid&&_.guid===w.guid){f=!0;break}}O++}if(f){const c=await j.getDependency("mesh",O);if(i&&console.log(`Loaded Mesh "${c.name}"`,x,O,c,p),c.isMesh===!0){const _=c.geometry;return M.assignLODInformation(l.url,_,d,e,0),h(_)}else{const _=new Array;for(let S=0;S<c.children.length;S++){const B=c.children[S];if(B.isMesh===!0){const z=B.geometry;M.assignLODInformation(l.url,z,d,e,S),_.push(z)}}return h(_)}}else m&&console.warn("Could not find mesh with guid",w.guid,D.parser.json)}return h(null)});return this.previouslyLoaded.set(p,k),Q.use(k),await k}else if(t instanceof se){i&&console.log("Load texture from uri: "+x);const p=await new Ze().loadAsync(x);return p?(p.guid=a.guid,p.flipY=!1,p.needsUpdate=!0,p.colorSpace=t.colorSpace,i&&console.log(a,p)):m&&console.warn("failed loading",x),p}}else m&&console.warn(`Can not load LOD ${e}: no LOD info found for "${d}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,s,n,r){if(!e)return;e.userData||(e.userData={});const o=new ft(t,s,n,r);e.userData.LODS=o}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e?(m&&console.warn(`Copy texture settings
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}};let P=M;u(P,"registerTexture",(t,e,s,o,r)=>{if(x&&console.log("> Progressive: register texture",o,e.name,e.uuid,e,r),!e){x&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[Oe]=r);const i=r.guid;M.assignLODInformation(t,e,i,s,o),M.lodInfos.set(i,r),M.lowresCache.set(i,e)}),u(P,"registerMesh",(t,e,s,o,r,i)=>{var n;const l=s.geometry;if(!l){x&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),x&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},i,s),M.assignLODInformation(t,l,e,o,r),M.lodInfos.set(e,i);let d=M.lowresCache.get(e);d?d.push(s.geometry):d=[s.geometry],M.lowresCache.set(e,d),o>0&&!ee(s)&&$e(s,l);for(const a of V)(n=a.onRegisteredNewMesh)==null||n.call(a,s,i)}),u(P,"lodInfos",new Map),u(P,"previouslyLoaded",new Map),u(P,"lowresCache",new Map);class gt{constructor(e,s,o,r){u(this,"url"),u(this,"key"),u(this,"level"),u(this,"index"),this.url=e,this.key=s,this.level=o,r!=null&&(this.index=r)}}class ge{constructor(e,s){u(this,"frame_start"),u(this,"frame_capture_end"),u(this,"ready"),u(this,"_resolve"),u(this,"_signal"),u(this,"_resolved",!1),u(this,"_addedCount",0),u(this,"_resolvedCount",0),u(this,"_awaiting",[]),u(this,"_maxPromisesPerObject",1),u(this,"_currentFrame",0),u(this,"_seen",new WeakMap);var o;const r=Math.max(s.frames??2,2);this.frame_start=e,this.frame_capture_end=e+r,this.ready=new Promise(i=>{this._resolve=i}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=s.signal,(o=this._signal)==null||o.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,s.maxPromisesPerObject??1)}get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}update(e){var s;this._currentFrame=e,((s=this._signal)!=null&&s.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}add(e,s,o){if(this._resolved){x&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(s)){let r=this._seen.get(s);if(r>=this._maxPromisesPerObject){x&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(s,r+1)}else this._seen.set(s,1);this._awaiting.push(o),this._addedCount++,o.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(o),1)})}}resolveNow(){var e,s;this._resolved||(s=this._resolve)==null||s.call(this,{awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:((e=this._signal)==null?void 0:e.aborted)??!1})}}u(ge,"addPromise",(t,e,s,o)=>{o.forEach(r=>{r.add(t,e,s)})});const N=re("debugprogressive"),ft=re("noprogressive"),Se=Symbol("Needle:LODSManager"),Pe=Symbol("Needle:LODState"),q=Symbol("Needle:CurrentLOD"),T={mesh_lod:-1,texture_lod:-1};var A,X,Te,K,ie,fe,H;const E=class{constructor(t,e){u(this,"renderer"),u(this,"context"),u(this,"projectionScreenMatrix",new Ce),u(this,"targetTriangleDensity",2e5),u(this,"skinnedMeshAutoUpdateBoundsInterval",30),u(this,"updateInterval","auto"),Y(this,A,1),u(this,"pause",!1),u(this,"manual",!1),u(this,"_newPromiseGroups",[]),u(this,"_promiseGroupIds",0),u(this,"_lodchangedlisteners",[]),Y(this,X,void 0),Y(this,Te,new Ze),Y(this,K,0),Y(this,ie,0),Y(this,fe,0),Y(this,H,0),u(this,"_fpsBuffer",[60,60,60,60,60]),u(this,"_sphere",new tt),u(this,"_tempBox",new Ie),u(this,"_tempBox2",new Ie),u(this,"tempMatrix",new Ce),u(this,"_tempWorldPosition",new z),u(this,"_tempBoxSize",new z),u(this,"_tempBox2Size",new z),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Pe]}static addPlugin(t){V.push(t)}static removePlugin(t){const e=V.indexOf(t);e>=0&&V.splice(e,1)}static get(t,e){if(t[Se])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Se];const s=new E(t,{engine:"unknown",...e});return t[Se]=s,s}get plugins(){return V}awaitLoading(t){const e=this._promiseGroupIds++,s=new ge(v(this,K),{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),dt()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(v(this,K))}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(v(this,X))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;U(this,X,this.renderer.render);const e=this;_e(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,U(e,K,v(e,K)+1),U(e,ie,v(e,Te).getDelta()),U(e,fe,v(e,fe)+v(e,ie)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/v(e,ie)),U(e,H,e._fpsBuffer.reduce((n,l)=>n+l)/e._fpsBuffer.length),N&&v(e,K)%200===0&&console.log("FPS",Math.round(v(e,H)),"Interval:",v(e,A)));const i=t++;v(e,X).call(this,s,o),e.onAfterRender(s,o,i)}}disable(){v(this,X)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=v(this,X),U(this,X,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const i=o[0].material;(i.name==="EffectMaterial"||i.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ft||(this.updateInterval==="auto"?v(this,H)<40&&v(this,A)<10?(U(this,A,v(this,A)+1),N&&console.warn("\u2193 Reducing LOD updates",v(this,A),v(this,H).toFixed(0))):v(this,H)>=60&&v(this,A)>1&&(U(this,A,v(this,A)-1),N&&console.warn("\u2191 Increasing LOD updates",v(this,A),v(this,H).toFixed(0))):U(this,A,this.updateInterval),v(this,A)>0&&v(this,K)%v(this,A)!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){var s,o;const r=this.renderer.renderLists.get(t,0),i=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const a of i){if(a.material&&(((s=a.geometry)==null?void 0:s.type)==="BoxGeometry"||((o=a.geometry)==null?void 0:o.type)==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){N&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(N==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const y=Math.random()*16777215,m=new et({color:y});a.object.material=m}const h=a.object;(h instanceof J||h.isMesh)&&this.updateLODs(t,e,h,n)}const l=r.transparent;for(const a of l){const h=a.object;(h instanceof J||h.isMesh)&&this.updateLODs(t,e,h,n)}const d=r.transmissive;for(const a of d){const h=a.object;(h instanceof J||h.isMesh)&&this.updateLODs(t,e,h,n)}}updateLODs(t,e,s,o){var r,i;s.userData||(s.userData={});let n=s[Pe];if(n||(n=new mt,s[Pe]=n),n.frames++<2)return;for(const d of V)(r=d.onBeforeUpdateLOD)==null||r.call(d,this.renderer,t,e,s);const l=E.overrideGlobalLodLevel!==void 0?E.overrideGlobalLodLevel:Q;l>=0?(T.mesh_lod=l,T.texture_lod=l):(this.calculateLodLevel(e,s,n,o,T),T.mesh_lod=Math.round(T.mesh_lod),T.texture_lod=Math.round(T.texture_lod)),T.mesh_lod>=0&&this.loadProgressiveMeshes(s,T.mesh_lod),s.material&&T.texture_lod>=0&&this.loadProgressiveTextures(s.material,T.texture_lod),x&&s.material&&!s.isGizmo&&Fe(s.material);for(const d of V)(i=d.onAfterUpdatedLOD)==null||i.call(d,this.renderer,t,e,s,T);n.lastLodLevel_Mesh=T.mesh_lod,n.lastLodLevel_Texture=T.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let s=!1;(t[q]===void 0||e<t[q])&&(s=!0);const o=t["DEBUG:LOD"];if(o!=null&&(s=t[q]!=o,e=o),s){t[q]=e;const r=P.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(n=>n({type:"texture",level:e,object:t}))});ge.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[q]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[q]!=o,e=o),s){t[q]=e;const r=t.geometry,i=P.assignMeshLOD(t,e).then(n=>(n&&t[q]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),n));return ge.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,i=(s.y+o.y)*.5;return this._tempPtInside.set(r,i,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,o,r){var i,n,l;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let d=10+1,a=!1;if(N&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const h=(i=P.getMeshLODExtension(e.geometry))==null?void 0:i.lods,y=P.getPrimitiveIndex(e.geometry),m=h&&h.length>0,w=P.getMaterialMinMaxLODsCount(e.material),b=w?.min_count!=1/0&&w.min_count>0&&w.max_count>0;if(!m&&!b){r.mesh_lod=0,r.texture_lod=0;return}m||(a=!0,d=0);const G=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let L=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const p=e;if(!p.boundingBox)p.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0&&s.frames%this.skinnedMeshAutoUpdateBoundsInterval===0){const f=ee(p),F=p.geometry;f&&(p.geometry=f),p.computeBoundingBox(),p.geometry=F}L=p.boundingBox}if(L){const p=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(L),this._tempBox.applyMatrix4(e.matrixWorld),p.isPerspectiveCamera&&E.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&p.isPerspectiveCamera&&p.fov>70){const c=this._tempBox.min,g=this._tempBox.max;let S=c.x,C=c.y,I=g.x,te=g.y;const ne=2,ve=1.5,ae=(c.x+g.x)*.5,le=(c.y+g.y)*.5;S=(S-ae)*ne+ae,C=(C-le)*ne+le,I=(I-ae)*ne+ae,te=(te-le)*ne+le;const Ke=S<0&&I>0?0:Math.min(Math.abs(c.x),Math.abs(g.x)),He=C<0&&te>0?0:Math.min(Math.abs(c.y),Math.abs(g.y)),xe=Math.max(Ke,He);s.lastCentrality=(ve-xe)*(ve-xe)*(ve-xe)}else s.lastCentrality=1;const f=this._tempBox.getSize(this._tempBoxSize);f.multiplyScalar(.5),screen.availHeight>0&&G>0&&f.multiplyScalar(G/screen.availHeight),t.isPerspectiveCamera?f.x*=t.aspect:t.isOrthographicCamera;const F=t.matrixWorldInverse,D=this._tempBox2;D.copy(L),D.applyMatrix4(e.matrixWorld),D.applyMatrix4(F);const R=D.getSize(this._tempBox2Size),O=Math.max(R.x,R.y);if(Math.max(f.x,f.y)!=0&&O!=0&&(f.z=R.z/Math.max(R.x,R.y)*Math.max(f.x,f.y)),s.lastScreenCoverage=Math.max(f.x,f.y,f.z),s.lastScreenspaceVolume.copy(f),s.lastScreenCoverage*=s.lastCentrality,N&&E.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const g=E.corner0,S=E.corner1,C=E.corner2,I=E.corner3;g.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=g.x,C.copy(this._tempBox.max),C.y=g.y,I.copy(this._tempBox.max);const te=(g.z+I.z)*.5;g.z=S.z=C.z=I.z=te,g.applyMatrix4(c),S.applyMatrix4(c),C.applyMatrix4(c),I.applyMatrix4(c),E.debugDrawLine(g,S,255),E.debugDrawLine(g,C,255),E.debugDrawLine(S,I,255),E.debugDrawLine(C,I,255)}let _=999;if(h&&s.lastScreenCoverage>0)for(let c=0;c<h.length;c++){const g=h[c];if((((n=g.densities)==null?void 0:n[y])||g.density||1e-5)/s.lastScreenCoverage<o){_=c;break}}_<d&&(d=_,a=!0)}if(a?r.mesh_lod=d:r.mesh_lod=s.lastLodLevel_Mesh,N&&r.mesh_lod!=s.lastLodLevel_Mesh){const p=h?.[r.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (${p.density.toFixed(0)}) - ${e.name}`)}if(b){const p="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=w.max_count-1,N){const f=w.lods[w.max_count-1];N&&console.log(`First Texture LOD ${r.texture_lod} (${f.max_height}px) - ${e.name}`)}}else{const f=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let F=s.lastScreenCoverage*4;((l=this.context)==null?void 0:l.engine)==="model-viewer"&&(F*=1.5);const D=G/window.devicePixelRatio*F;let R=!1;for(let O=w.lods.length-1;O>=0;O--){const _=w.lods[O];if(!(p&&_.max_height>=2048)&&!(ut()&&_.max_height>4096)&&(_.max_height>D||!R&&O===0)){if(R=!0,r.texture_lod=O,r.texture_lod<s.lastLodLevel_Texture){const c=_.max_height;N&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${c}px
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${e.name}`)}break}}}}else r.texture_lod=0}};let
|
|
8
|
-
`,t.getAttribute("src"));let e=null,s=null,
|
|
5
|
+
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}};let P=M;u(P,"registerTexture",(t,e,s,n,r)=>{if(m&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,r),!e){m&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[Oe]=r);const o=r.guid;M.assignLODInformation(t,e,o,s,n),M.lodInfos.set(o,r),M.lowresCache.set(o,e)}),u(P,"registerMesh",(t,e,s,n,r,o)=>{var i;const l=s.geometry;if(!l){m&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),m&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},o,s),M.assignLODInformation(t,l,e,n,r),M.lodInfos.set(e,o);let d=M.lowresCache.get(e);d?d.push(s.geometry):d=[s.geometry],M.lowresCache.set(e,d),n>0&&!te(s)&&Ne(s,l);for(const a of q)(i=a.onRegisteredNewMesh)==null||i.call(a,s,o)}),u(P,"lodInfos",new Map),u(P,"previouslyLoaded",new Map),u(P,"lowresCache",new Map),u(P,"queue",new ct(100,{debug:m!=!1}));class ft{constructor(e,s,n,r){u(this,"url"),u(this,"key"),u(this,"level"),u(this,"index"),this.url=e,this.key=s,this.level=n,r!=null&&(this.index=r)}}class ge{constructor(e,s){u(this,"frame_start"),u(this,"frame_capture_end"),u(this,"ready"),u(this,"_resolve"),u(this,"_signal"),u(this,"_resolved",!1),u(this,"_addedCount",0),u(this,"_resolvedCount",0),u(this,"_awaiting",[]),u(this,"_maxPromisesPerObject",1),u(this,"_currentFrame",0),u(this,"_seen",new WeakMap);var n;const r=Math.max(s.frames??2,2);this.frame_start=e,this.frame_capture_end=e+r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=s.signal,(n=this._signal)==null||n.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,s.maxPromisesPerObject??1)}get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}update(e){var s;this._currentFrame=e,((s=this._signal)!=null&&s.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}add(e,s,n){if(this._resolved){m&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(s)){let r=this._seen.get(s);if(r>=this._maxPromisesPerObject){m&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(s,r+1)}else this._seen.set(s,1);this._awaiting.push(n),this._addedCount++,n.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(n),1)})}}resolveNow(){var e,s;this._resolved||(s=this._resolve)==null||s.call(this,{awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:((e=this._signal)==null?void 0:e.aborted)??!1})}}u(ge,"addPromise",(t,e,s,n)=>{n.forEach(r=>{r.add(t,e,s)})});const $=re("debugprogressive"),mt=re("noprogressive"),Se=Symbol("Needle:LODSManager"),Pe=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),A={mesh_lod:-1,texture_lod:-1};var E,X,Te,K,oe,fe,H;const b=class{constructor(t,e){u(this,"renderer"),u(this,"context"),u(this,"projectionScreenMatrix",new Ce),u(this,"targetTriangleDensity",2e5),u(this,"skinnedMeshAutoUpdateBoundsInterval",30),u(this,"updateInterval","auto"),Y(this,E,1),u(this,"pause",!1),u(this,"manual",!1),u(this,"_newPromiseGroups",[]),u(this,"_promiseGroupIds",0),u(this,"_lodchangedlisteners",[]),Y(this,X,void 0),Y(this,Te,new Je),Y(this,K,0),Y(this,oe,0),Y(this,fe,0),Y(this,H,0),u(this,"_fpsBuffer",[60,60,60,60,60]),u(this,"_sphere",new tt),u(this,"_tempBox",new ke),u(this,"_tempBox2",new ke),u(this,"tempMatrix",new Ce),u(this,"_tempWorldPosition",new W),u(this,"_tempBoxSize",new W),u(this,"_tempBox2Size",new W),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Pe]}static addPlugin(t){q.push(t)}static removePlugin(t){const e=q.indexOf(t);e>=0&&q.splice(e,1)}static get(t,e){if(t[Se])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Se];const s=new b(t,{engine:"unknown",...e});return t[Se]=s,s}get plugins(){return q}awaitLoading(t){const e=this._promiseGroupIds++,s=new ge(y(this,K),{...t});this._newPromiseGroups.push(s);const n=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),dt()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(y(this,K))}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(y(this,X))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;U(this,X,this.renderer.render);const e=this;Le(this.renderer),this.renderer.render=function(s,n){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,U(e,K,y(e,K)+1),U(e,oe,y(e,Te).getDelta()),U(e,fe,y(e,fe)+y(e,oe)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/y(e,oe)),U(e,H,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),$&&y(e,K)%200===0&&console.log("FPS",Math.round(y(e,H)),"Interval:",y(e,E)));const o=t++;y(e,X).call(this,s,n),e.onAfterRender(s,n,o)}}disable(){y(this,X)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=y(this,X),U(this,X,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const n=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(n.length===1){const o=n[0].material;(o.name==="EffectMaterial"||o.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(mt||(this.updateInterval==="auto"?y(this,H)<40&&y(this,E)<10?(U(this,E,y(this,E)+1),$&&console.warn("\u2193 Reducing LOD updates",y(this,E),y(this,H).toFixed(0))):y(this,H)>=60&&y(this,E)>1&&(U(this,E,y(this,E)-1),$&&console.warn("\u2191 Increasing LOD updates",y(this,E),y(this,H).toFixed(0))):U(this,E,this.updateInterval),y(this,E)>0&&y(this,K)%y(this,E)!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){var s,n;const r=this.renderer.renderLists.get(t,0),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const i=this.targetTriangleDensity;for(const a of o){if(a.material&&(((s=a.geometry)==null?void 0:s.type)==="BoxGeometry"||((n=a.geometry)==null?void 0:n.type)==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){$&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if($==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const x=Math.random()*16777215,p=new et({color:x});a.object.material=p}const g=a.object;(g instanceof Z||g.isMesh)&&this.updateLODs(t,e,g,i)}const l=r.transparent;for(const a of l){const g=a.object;(g instanceof Z||g.isMesh)&&this.updateLODs(t,e,g,i)}const d=r.transmissive;for(const a of d){const g=a.object;(g instanceof Z||g.isMesh)&&this.updateLODs(t,e,g,i)}}updateLODs(t,e,s,n){var r,o;s.userData||(s.userData={});let i=s[Pe];if(i||(i=new pt,s[Pe]=i),i.frames++<2)return;for(const d of q)(r=d.onBeforeUpdateLOD)==null||r.call(d,this.renderer,t,e,s);const l=b.overrideGlobalLodLevel!==void 0?b.overrideGlobalLodLevel:J;l>=0?(A.mesh_lod=l,A.texture_lod=l):(this.calculateLodLevel(e,s,i,n,A),A.mesh_lod=Math.round(A.mesh_lod),A.texture_lod=Math.round(A.texture_lod)),A.mesh_lod>=0&&this.loadProgressiveMeshes(s,A.mesh_lod),s.material&&A.texture_lod>=0&&this.loadProgressiveTextures(s.material,A.texture_lod),m&&s.material&&!s.isGizmo&&Ue(s.material);for(const d of q)(o=d.onAfterUpdatedLOD)==null||o.call(d,this.renderer,t,e,s,A);i.lastLodLevel_Mesh=A.mesh_lod,i.lastLodLevel_Texture=A.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let s=!1;(t[V]===void 0||e<t[V])&&(s=!0);const n=t["DEBUG:LOD"];if(n!=null&&(s=t[V]!=n,e=n),s){t[V]=e;const r=P.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(i=>i({type:"texture",level:e,object:t}))});ge.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[V]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(s=t[V]!=n,e=n),s){t[V]=e;const r=t.geometry,o=P.assignMeshLOD(t,e).then(i=>(i&&t[V]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),i));return ge.addPromise("mesh",t,o,this._newPromiseGroups),o}return Promise.resolve(null)}static isInside(t,e){const s=t.min,n=t.max,r=(s.x+n.x)*.5,o=(s.y+n.y)*.5;return this._tempPtInside.set(r,o,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,n,r){var o,i,l;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let d=10+1,a=!1;if($&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const g=(o=P.getMeshLODExtension(e.geometry))==null?void 0:o.lods,x=P.getPrimitiveIndex(e.geometry),p=g&&g.length>0,L=P.getMaterialMinMaxLODsCount(e.material),Q=L?.min_count!=1/0&&L.min_count>0&&L.max_count>0;if(!p&&!Q){r.mesh_lod=0,r.texture_lod=0;return}p||(a=!0,d=0);const w=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let k=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[b.$skinnedMeshBoundsOffset]){const T=b.skinnedMeshBoundsFrameOffsetCounter++;h[b.$skinnedMeshBoundsOffset]=T}const v=h[b.$skinnedMeshBoundsOffset];if((s.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const T=te(h),G=h.geometry;T&&(h.geometry=T),h.computeBoundingBox(),h.geometry=G}}k=h.boundingBox}if(k){const h=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 f=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(k),this._tempBox.applyMatrix4(e.matrixWorld),h.isPerspectiveCamera&&b.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&h.isPerspectiveCamera&&h.fov>70){const f=this._tempBox.min,c=this._tempBox.max;let _=f.x,S=f.y,B=c.x,z=c.y;const ie=2,ve=1.5,ae=(f.x+c.x)*.5,le=(f.y+c.y)*.5;_=(_-ae)*ie+ae,S=(S-le)*ie+le,B=(B-ae)*ie+ae,z=(z-le)*ie+le;const Ke=_<0&&B>0?0:Math.min(Math.abs(f.x),Math.abs(c.x)),He=S<0&&z>0?0:Math.min(Math.abs(f.y),Math.abs(c.y)),ye=Math.max(Ke,He);s.lastCentrality=(ve-ye)*(ve-ye)*(ve-ye)}else s.lastCentrality=1;const v=this._tempBox.getSize(this._tempBoxSize);v.multiplyScalar(.5),screen.availHeight>0&&w>0&&v.multiplyScalar(w/screen.availHeight),t.isPerspectiveCamera?v.x*=t.aspect:t.isOrthographicCamera;const T=t.matrixWorldInverse,G=this._tempBox2;G.copy(k),G.applyMatrix4(e.matrixWorld),G.applyMatrix4(T);const D=G.getSize(this._tempBox2Size),j=Math.max(D.x,D.y);if(Math.max(v.x,v.y)!=0&&j!=0&&(v.z=D.z/Math.max(D.x,D.y)*Math.max(v.x,v.y)),s.lastScreenCoverage=Math.max(v.x,v.y,v.z),s.lastScreenspaceVolume.copy(v),s.lastScreenCoverage*=s.lastCentrality,$&&b.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const c=b.corner0,_=b.corner1,S=b.corner2,B=b.corner3;c.copy(this._tempBox.min),_.copy(this._tempBox.max),_.x=c.x,S.copy(this._tempBox.max),S.y=c.y,B.copy(this._tempBox.max);const z=(c.z+B.z)*.5;c.z=_.z=S.z=B.z=z,c.applyMatrix4(f),_.applyMatrix4(f),S.applyMatrix4(f),B.applyMatrix4(f),b.debugDrawLine(c,_,255),b.debugDrawLine(c,S,255),b.debugDrawLine(_,B,255),b.debugDrawLine(S,B,255)}let O=999;if(g&&s.lastScreenCoverage>0)for(let f=0;f<g.length;f++){const c=g[f];if((((i=c.densities)==null?void 0:i[x])||c.density||1e-5)/s.lastScreenCoverage<n){O=f;break}}O<d&&(d=O,a=!0)}if(a?r.mesh_lod=d:r.mesh_lod=s.lastLodLevel_Mesh,$&&r.mesh_lod!=s.lastLodLevel_Mesh){const h=g?.[r.mesh_lod];h&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (${h.density.toFixed(0)}) - ${e.name}`)}if(Q){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=L.max_count-1,$){const v=L.lods[L.max_count-1];$&&console.log(`First Texture LOD ${r.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let T=s.lastScreenCoverage*4;((l=this.context)==null?void 0:l.engine)==="model-viewer"&&(T*=1.5);const G=w/window.devicePixelRatio*T;let D=!1;for(let j=L.lods.length-1;j>=0;j--){const O=L.lods[j];if(!(h&&O.max_height>=2048)&&!(ut()&&O.max_height>4096)&&(O.max_height>G||!D&&j===0)){if(D=!0,r.texture_lod=j,r.texture_lod<s.lastLodLevel_Texture){const f=O.max_height;$&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${f}px
|
|
6
|
+
Screensize: ${G.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${v.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else r.texture_lod=0}};let C=b;E=new WeakMap,X=new WeakMap,Te=new WeakMap,K=new WeakMap,oe=new WeakMap,fe=new WeakMap,H=new WeakMap,u(C,"debugDrawLine"),u(C,"overrideGlobalLodLevel"),u(C,"corner0",new W),u(C,"corner1",new W),u(C,"corner2",new W),u(C,"corner3",new W),u(C,"_tempPtInside",new W),u(C,"skinnedMeshBoundsFrameOffsetCounter",0),u(C,"$skinnedMeshBoundsOffset",Symbol("gltf-progressive-skinnedMeshBoundsOffset"));class pt{constructor(){u(this,"frames",0),u(this,"lastLodLevel_Mesh",-1),u(this,"lastLodLevel_Texture",-1),u(this,"lastScreenCoverage",0),u(this,"lastScreenspaceVolume",new W),u(this,"lastCentrality",0)}}const We=Symbol("NEEDLE_mesh_lod"),me=Symbol("NEEDLE_texture_lod");let pe=null;function Ae(){const t=vt();t&&(t.mapURLs(function(e){return ze(),e}),ze(),pe?.disconnect(),pe=new MutationObserver(e=>{e.forEach(s=>{s.addedNodes.forEach(n=>{n instanceof HTMLElement&&n.tagName.toLowerCase()==="model-viewer"&&Ve(n)})})}),pe.observe(document,{childList:!0,subtree:!0}))}function vt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ae()}),null)}function ze(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(t=>{Ve(t)})}const qe=new WeakSet;let yt=0;function Ve(t){if(!t||qe.has(t))return null;qe.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++yt+`
|
|
8
|
+
`,t.getAttribute("src"));let e=null,s=null,n=null;for(let r=t;r!=null;r=Object.getPrototypeOf(r)){const o=Object.getOwnPropertySymbols(r),i=o.find(a=>a.toString()=="Symbol(renderer)"),l=o.find(a=>a.toString()=="Symbol(scene)"),d=o.find(a=>a.toString()=="Symbol(needsRender)");!e&&i!=null&&(e=t[i].threeRenderer),!s&&l!=null&&(s=t[l]),!n&&d!=null&&(n=t[d])}if(e&&s){let r=function(){if(n){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}n?.call(t)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=C.get(e,{engine:"model-viewer"});return C.addPlugin(new xt),o.enable(),o.addEventListener("changed",()=>{n?.call(t)}),t.addEventListener("model-visibility",i=>{i.detail.visible&&n?.call(t)}),t.addEventListener("load",()=>{r()}),()=>{o.disable()}}return null}class xt{constructor(){u(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,s,n,r){this.tryParseMeshLOD(s,r),this.tryParseTextureLOD(s,r)}getUrl(e){if(!e)return null;let s=e.getAttribute("src");return s||(s=e.src),s||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),s}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,s){if(s[me]==!0)return;s[me]=!0;const n=this.tryGetCurrentGLTF(e),r=this.tryGetCurrentModelViewer(e),o=this.getUrl(r);if(o&&n&&s.material){let i=function(d){var a,g,x;if(d[me]==!0)return;d[me]=!0,d.userData&&(d.userData.LOD=-1);const p=Object.keys(d);for(let L=0;L<p.length;L++){const Q=p[L],w=d[Q];if(w?.isTexture===!0){const k=(g=(a=w.userData)==null?void 0:a.associations)==null?void 0:g.textures;if(k==null)continue;const h=n.parser.json.textures[k];if(!h){console.warn("Texture data not found for texture index "+k);continue}if((x=h?.extensions)!=null&&x[F]){const v=h.extensions[F];v&&o&&P.registerTexture(o,w,v.lods.length,k,v)}}}};const l=s.material;if(Array.isArray(l))for(const d of l)i(d);else i(l)}}tryParseMeshLOD(e,s){var n,r;if(s[We]==!0)return;s[We]=!0;const o=this.tryGetCurrentModelViewer(e),i=this.getUrl(o);if(!i)return;const l=(r=(n=s.userData)==null?void 0:n.gltfExtensions)==null?void 0:r[F];if(l&&i){const d=s.uuid;P.registerMesh(i,d,s,0,l.lods.length,l)}}}function Xe(t,e,s,n){Le(e),we(s),be(s,{progressive:!0,...n?.hints}),s.register(o=>new P(o,t));const r=C.get(e);return n?.enableLODsManager!==!1&&r.enable(),r}if(Ae(),!ht){const t={gltfProgressive:{useNeedleProgressive:Xe,LODsManager:C,configureLoader:be,getRaycastMesh:te,useRaycastMeshes:Fe}};if(!globalThis.Needle)globalThis.Needle=t;else for(const e in t)globalThis.Needle[e]=t[e]}export{F as EXTENSION_NAME,C as LODsManager,P as NEEDLE_progressive,Be as VERSION,we as addDracoAndKTX2Loaders,be as configureLoader,Le as createLoaders,te as getRaycastMesh,Ae as patchModelViewer,Ne as registerRaycastMesh,Ge as setDracoDecoderLocation,Re as setKTX2TranscoderLocation,Xe as useNeedleProgressive,Fe as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";var je=Object.defineProperty;var He=(n,t,e)=>t in n?je(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var c=(n,t,e)=>(He(n,typeof t!="symbol"?t+"":t,e),e),Ge=(n,t,e)=>{if(!t.has(n))throw TypeError("Cannot "+e)};var m=(n,t,e)=>(Ge(n,t,"read from private field"),e?e.call(n):t.get(n)),J=(n,t,e)=>{if(t.has(n))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(n):t.set(n,e)},W=(n,t,e,s)=>(Ge(n,t,"write to private field"),s?s.call(n,e):t.set(n,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("three"),Te=require("three/examples/jsm/loaders/GLTFLoader.js"),Je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Qe=require("three/examples/jsm/loaders/DRACOLoader.js"),Ze=require("three/examples/jsm/loaders/KTX2Loader.js"),ke="";globalThis.GLTF_PROGRESSIVE_VERSION=ke;console.debug("[gltf-progressive] version -");let X="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",se="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const et=X,tt=se,Fe=new URL(X+"draco_decoder.js");Fe.searchParams.append("range","true");fetch(Fe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${X} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),X===et&&$e("./include/draco/"),se===tt&&Ne("./include/ktx2/")}).finally(()=>{Ue()});function $e(n){X=n,I&&I[Oe]!=X?(console.debug("Updating Draco decoder path to "+n),I[Oe]=X,I.setDecoderPath(X),I.preload()):console.debug("Setting Draco decoder path to "+n)}function Ne(n){se=n,U&&U.transcoderPath!=se?(console.debug("Updating KTX2 transcoder path to "+n),U.setTranscoderPath(se),U.init()):console.debug("Setting KTX2 transcoder path to "+n)}const Oe=Symbol("dracoDecoderPath");let I,me,U;function Ue(){I||(I=new Qe.DRACOLoader,I[Oe]=X,I.setDecoderPath(X),I.setDecoderConfig({type:"js"}),I.preload()),U||(U=new Ze.KTX2Loader,U.setTranscoderPath(se),U.init()),me||(me=Je.MeshoptDecoder)}function Pe(n){return Ue(),n?U.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:I,ktx2Loader:U,meshoptDecoder:me}}function Ae(n){n.dracoLoader||n.setDRACOLoader(I),n.ktx2Loader||n.setKTX2Loader(U),n.meshoptDecoder||n.setMeshoptDecoder(me)}const be=new WeakMap;function Ce(n,t){let e=be.get(n);e?e=Object.assign(e,t):e=t,be.set(n,e)}const we=Te.GLTFLoader.prototype.load;function st(...n){const t=be.get(this);let e=n[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const r=(t==null?void 0:t.progressive)!==void 0?t.progressive:!0,o=t!=null&&t.usecase?t.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=s.toString()}return n[0]=e,we==null?void 0:we.call(this,...n)}Te.GLTFLoader.prototype.load=st;ce("debugprogressive");function ce(n){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function rt(n,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||n===void 0)return t;const e=n.lastIndexOf("/");if(e>=0){const s=n.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}let ne;function ot(){return ne!==void 0||(ne=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),ce("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ne)),ne}function it(){if(typeof window>"u")return!1;const n=new URL(window.location.href),t=n.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(n.hostname);return n.hostname==="127.0.0.1"||t}const nt=typeof window>"u"&&typeof document>"u",Se=Symbol("needle:raycast-mesh");function oe(n){return(n==null?void 0:n[Se])instanceof p.BufferGeometry?n[Se]:null}function Ve(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!oe(n)){const s=at(t);s.userData={isRaycastMesh:!0},n[Se]=s}}function ze(n=!0){if(n){if(ae)return;const t=ae=p.Mesh.prototype.raycast;p.Mesh.prototype.raycast=function(e,s){const i=this,r=oe(i);let o;r&&i.isMesh&&(o=i.geometry,i.geometry=r),t.call(this,e,s),o&&(i.geometry=o)}}else{if(!ae)return;p.Mesh.prototype.raycast=ae,ae=null}}let ae=null;function at(n){const t=new p.BufferGeometry;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const Z=new Array,L=ce("debugprogressive");let ge,te=-1;if(L){let n=function(){te+=1,te>=t&&(te=-1),console.log(`Toggle LOD level [${te}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(ge=!ge,console.log(`Toggle wireframe [${ge}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(te=s,console.log(`Set LOD level to [${te}]`))})}function We(n){if(L)if(Array.isArray(n))for(const t of n)We(t);else n&&"wireframe"in n&&(n.wireframe=ge===!0)}const q="NEEDLE_progressive",ve=Symbol("needle-progressive-texture"),S=class{constructor(t,e){c(this,"parser");c(this,"url");c(this,"_isLoadingMesh");c(this,"loadMesh",t=>{var s,i;if(this._isLoadingMesh)return null;const e=(i=(s=this.parser.json.meshes[t])==null?void 0:s.extensions)==null?void 0:i[q];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>{var o;return this._isLoadingMesh=!1,r&&S.registerMesh(this.url,e.guid,r,(o=e.lods)==null?void 0:o.length,0,e),r})):null});L&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return q}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var s;const e=(s=this.getAssignedLODInformation(t))==null?void 0:s.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,i="LODS:minmax",r=t[i];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[i]=e,e}if(L==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;(u==null?void 0:u.isTexture)===!0&&o(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];(l==null?void 0:l.isTexture)===!0&&o(l,e)}return t[i]=e,e;function o(a,l){const u=s.getAssignedLODInformation(a);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){l.min_count=Math.min(l.min_count,d.lods.length),l.max_count=Math.max(l.max_count,d.lods.length);for(let h=0;h<d.lods.length;h++){const g=d.lods[h];g.width&&(l.lods[h]=l.lods[h]||{min_height:1/0,max_height:0},l.lods[h].min_height=Math.min(l.lods[h].min_height,g.height),l.lods[h].max_height=Math.max(l.lods[h].max_height,g.height))}}}}}static hasLODLevelAvailable(t,e){var 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 a=t[o];if(a&&a.isTexture&&this.hasLODLevelAvailable(a,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 s,i;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&(r=s==null?void 0:s.userData)!=null&&r.LODS){const o=s.userData.LODS;if(i=this.lodInfos.get(o.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 s;if(!t)return Promise.resolve(null);if(t instanceof p.Mesh||t.isMesh===!0){const i=t.geometry,r=this.getAssignedLODInformation(i);if(!r)return Promise.resolve(null);for(const o of Z)(s=o.onBeforeGetLODMesh)==null||s.call(o,t,e);return t["LOD:requested level"]=e,S.getOrLoadLOD(i,e).then(o=>{if(Array.isArray(o)){const a=r.index||0;o=o[a]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&i!=o&&((o==null?void 0:o.isBufferGeometry)?t.geometry=o:L&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else L&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const i=new Array;for(const r of s.material){const o=this.assignTextureLOD(r,e);i.push(o)}return Promise.all(i).then(r=>{const o=new Array;for(const a of r)Array.isArray(a)&&o.push(...a);return o})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,i=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const o=s;for(const a of Object.keys(o.uniforms)){const l=o.uniforms[a].value;if((l==null?void 0:l.isTexture)===!0){const u=this.assignTextureLODForSlot(l,e,s,a).then(d=>(d&&o.uniforms[a].value!=d&&(o.uniforms[a].value=d,o.uniformsNeedUpdate=!0),d));i.push(u),r.push(a)}}}else for(const o of Object.keys(s)){const a=s[o];if((a==null?void 0:a.isTexture)===!0){const l=this.assignTextureLODForSlot(a,e,s,o);i.push(l),r.push(o)}}return Promise.all(i).then(o=>{const a=new Array;for(let l=0;l<o.length;l++){const u=o[l],d=r[l];u&&u.isTexture===!0?a.push({material:s,slot:d,texture:u,level:e}):a.push({material:s,slot:d,texture:null,level:e})}return a})}if(t instanceof p.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,i){return(t==null?void 0:t.isTexture)!==!0?Promise.resolve(null):i==="glyphMap"?Promise.resolve(t):S.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if((r==null?void 0:r.isTexture)===!0){if(r!=t&&s&&i){const o=s[i];if(o&&!L){const a=this.getAssignedLODInformation(o);if(a&&(a==null?void 0:a.level)<e)return L==="verbose"&&console.warn("Assigned texture level is already higher: ",a.level,e,s,o,r),null}s[i]=r}return r}else L=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}afterRoot(t){var e,s;return L&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((i,r)=>{var o;if(i!=null&&i.extensions){const a=i==null?void 0:i.extensions[q];if(a){if(!a.lods){L&&console.warn("Texture has no LODs",a);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const d=this.parser.associations.get(u);(d==null?void 0:d.textures)===r&&(l=!0,S.registerTexture(this.url,u,(o=a.lods)==null?void 0:o.length,r,a))}l||this.parser.getDependency("texture",r).then(u=>{var d;u&&S.registerTexture(this.url,u,(d=a.lods)==null?void 0:d.length,r,a)})}}}),(s=this.parser.json.meshes)==null||s.forEach((i,r)=>{if(i!=null&&i.extensions){const o=i==null?void 0:i.extensions[q];if(o&&o.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const l=this.parser.associations.get(a);(l==null?void 0:l.meshes)===r&&S.registerMesh(this.url,o.guid,a,o.lods.length,l.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var a,l,u,d;const s=L=="verbose",i=t.userData.LODS;if(!i)return null;const r=i==null?void 0:i.key;let o;if(t.isTexture===!0){const h=t;h.source&&h.source[ve]&&(o=h.source[ve])}if(o||(o=S.lodInfos.get(r)),o){if(e>0){let w=!1;const O=Array.isArray(o.lods);if(O&&e>=o.lods.length?w=!0:O||(w=!0),w)return this.lowresCache.get(r)}const h=Array.isArray(o.lods)?(a=o.lods[e])==null?void 0:a.path:o.lods;if(!h)return L&&!o["missing:uri"]&&(o["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,o)),null;const g=rt(i.url,h);if(g.endsWith(".glb")||g.endsWith(".gltf")){if(!o.guid)return console.warn("missing pointer for glb/gltf texture",o),null;const w=g+"_"+o.guid,O=this.previouslyLoaded.get(w);if(O!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${w}`);let v=await O.catch(y=>(console.error(`Error loading LOD ${e} from ${g}
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
1
|
+
"use strict";var je=Object.defineProperty;var He=(o,t,e)=>t in o?je(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var u=(o,t,e)=>(He(o,typeof t!="symbol"?t+"":t,e),e),Ee=(o,t,e)=>{if(!t.has(o))throw TypeError("Cannot "+e)};var L=(o,t,e)=>(Ee(o,t,"read from private field"),e?e.call(o):t.get(o)),j=(o,t,e)=>{if(t.has(o))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(o):t.set(o,e)},z=(o,t,e,s)=>(Ee(o,t,"write to private field"),s?s.call(o,e):t.set(o,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("three"),Pe=require("three/examples/jsm/loaders/GLTFLoader.js"),Qe=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Je=require("three/examples/jsm/loaders/DRACOLoader.js"),Ze=require("three/examples/jsm/loaders/KTX2Loader.js"),Re="";globalThis.GLTF_PROGRESSIVE_VERSION=Re;console.debug("[gltf-progressive] version -");let X="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",se="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const et=X,tt=se,Ie=new URL(X+"draco_decoder.js");Ie.searchParams.append("range","true");fetch(Ie,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${X} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),X===et&&Fe("./include/draco/"),se===tt&&Ne("./include/ktx2/")}).finally(()=>{Ue()});function Fe(o){X=o,I&&I[Oe]!=X?(console.debug("Updating Draco decoder path to "+o),I[Oe]=X,I.setDecoderPath(X),I.preload()):console.debug("Setting Draco decoder path to "+o)}function Ne(o){se=o,U&&U.transcoderPath!=se?(console.debug("Updating KTX2 transcoder path to "+o),U.setTranscoderPath(se),U.init()):console.debug("Setting KTX2 transcoder path to "+o)}const Oe=Symbol("dracoDecoderPath");let I,me,U;function Ue(){I||(I=new Je.DRACOLoader,I[Oe]=X,I.setDecoderPath(X),I.setDecoderConfig({type:"js"}),I.preload()),U||(U=new Ze.KTX2Loader,U.setTranscoderPath(se),U.init()),me||(me=Qe.MeshoptDecoder)}function Te(o){return Ue(),o?U.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:I,ktx2Loader:U,meshoptDecoder:me}}function Ae(o){o.dracoLoader||o.setDRACOLoader(I),o.ktx2Loader||o.setKTX2Loader(U),o.meshoptDecoder||o.setMeshoptDecoder(me)}const be=new WeakMap;function Ce(o,t){let e=be.get(o);e?e=Object.assign(e,t):e=t,be.set(o,e)}const we=Pe.GLTFLoader.prototype.load;function st(...o){const t=be.get(this);let e=o[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const r=(t==null?void 0:t.progressive)!==void 0?t.progressive:!0,i=t!=null&&t.usecase?t.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=s.toString()}return o[0]=e,we==null?void 0:we.call(this,...o)}Pe.GLTFLoader.prototype.load=st;ue("debugprogressive");function ue(o){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(o);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function rt(o,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||o===void 0)return t;const e=o.lastIndexOf("/");if(e>=0){const s=o.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}let oe;function it(){return oe!==void 0||(oe=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),ue("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",oe)),oe}function nt(){if(typeof window>"u")return!1;const o=new URL(window.location.href),t=o.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(o.hostname);return o.hostname==="127.0.0.1"||t}class ot{constructor(t=100,e={}){u(this,"_running",new Map);u(this,"_queue",[]);u(this,"debug",!1);u(this,"tick",()=>{this.internalUpdate(),setTimeout(this.tick,10)});this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise for key ${t} finished, running: ${this._running.size}, waiting: ${this._queue.length}`)}),this.debug&&console.debug(`[PromiseQueue]: Adding promise for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:n}=this._queue.shift();n({use:r=>this.add(s,r)})}}}const at=typeof window>"u"&&typeof document>"u",Se=Symbol("needle:raycast-mesh");function ie(o){return(o==null?void 0:o[Se])instanceof y.BufferGeometry?o[Se]:null}function Ve(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!ie(o)){const s=lt(t);s.userData={isRaycastMesh:!0},o[Se]=s}}function qe(o=!0){if(o){if(ae)return;const t=ae=y.Mesh.prototype.raycast;y.Mesh.prototype.raycast=function(e,s){const n=this,r=ie(n);let i;r&&n.isMesh&&(i=n.geometry,n.geometry=r),t.call(this,e,s),i&&(n.geometry=i)}}else{if(!ae)return;y.Mesh.prototype.raycast=ae,ae=null}}let ae=null;function lt(o){const t=new y.BufferGeometry;for(const e in o.attributes)t.setAttribute(e,o.getAttribute(e));return t.setIndex(o.getIndex()),t}const Q=new Array,x=ue("debugprogressive");let ge,te=-1;if(x){let o=function(){te+=1,te>=t&&(te=-1),console.log(`Toggle LOD level [${te}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(ge=!ge,console.log(`Toggle wireframe [${ge}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(te=s,console.log(`Set LOD level to [${te}]`))})}function ze(o){if(x)if(Array.isArray(o))for(const t of o)ze(t);else o&&"wireframe"in o&&(o.wireframe=ge===!0)}const W="NEEDLE_progressive",Me=Symbol("needle-progressive-texture"),O=class{constructor(t,e){u(this,"parser");u(this,"url");u(this,"_isLoadingMesh");u(this,"loadMesh",t=>{var s,n;if(this._isLoadingMesh)return null;const e=(n=(s=this.parser.json.meshes[t])==null?void 0:s.extensions)==null?void 0:n[W];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>{var i;return this._isLoadingMesh=!1,r&&O.registerMesh(this.url,e.guid,r,(i=e.lods)==null?void 0:i.length,0,e),r})):null});x&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return W}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){var s;const e=(s=this.getAssignedLODInformation(t))==null?void 0:s.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,n="LODS:minmax",r=t[n];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[n]=e,e}if(x==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const c=a.uniforms[l].value;(c==null?void 0:c.isTexture)===!0&&i(c,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];(l==null?void 0:l.isTexture)===!0&&i(l,e)}return t[n]=e,e;function i(a,l){const c=s.getAssignedLODInformation(a);if(c){const d=s.lodInfos.get(c.key);if(d&&d.lods){l.min_count=Math.min(l.min_count,d.lods.length),l.max_count=Math.max(l.max_count,d.lods.length);for(let p=0;p<d.lods.length;p++){const f=d.lods[p];f.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,f.height),l.lods[p].max_height=Math.max(l.lods[p].max_height,f.height))}}}}}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const i of t)if(this.hasLODLevelAvailable(i,e))return!0;return!1}if(t.isMaterial===!0){for(const i of Object.keys(t)){const a=t[i];if(a&&a.isTexture&&this.hasLODLevelAvailable(a,e))return!0}return!1}else if(t.isGroup===!0){for(const i of t.children)if(i.isMesh===!0&&this.hasLODLevelAvailable(i,e))return!0}let s,n;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&(r=s==null?void 0:s.userData)!=null&&r.LODS){const i=s.userData.LODS;if(n=this.lodInfos.get(i.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(t,e){var s;if(!t)return Promise.resolve(null);if(t instanceof y.Mesh||t.isMesh===!0){const n=t.geometry,r=this.getAssignedLODInformation(n);if(!r)return Promise.resolve(null);for(const i of Q)(s=i.onBeforeGetLODMesh)==null||s.call(i,t,e);return t["LOD:requested level"]=e,O.getOrLoadLOD(n,e).then(i=>{if(Array.isArray(i)){const a=r.index||0;i=i[a]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],i&&n!=i&&((i==null?void 0:i.isBufferGeometry)?t.geometry=i:x&&console.error("Invalid LOD geometry",i))),i}).catch(i=>(console.error("Error loading mesh LOD",t,i),null))}else x&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const n=new Array;for(const r of s.material){const i=this.assignTextureLOD(r,e);n.push(i)}return Promise.all(n).then(r=>{const i=new Array;for(const a of r)Array.isArray(a)&&i.push(...a);return i})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,n=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const i=s;for(const a of Object.keys(i.uniforms)){const l=i.uniforms[a].value;if((l==null?void 0:l.isTexture)===!0){const c=this.assignTextureLODForSlot(l,e,s,a).then(d=>(d&&i.uniforms[a].value!=d&&(i.uniforms[a].value=d,i.uniformsNeedUpdate=!0),d));n.push(c),r.push(a)}}}else for(const i of Object.keys(s)){const a=s[i];if((a==null?void 0:a.isTexture)===!0){const l=this.assignTextureLODForSlot(a,e,s,i);n.push(l),r.push(i)}}return Promise.all(n).then(i=>{const a=new Array;for(let l=0;l<i.length;l++){const c=i[l],d=r[l];c&&c.isTexture===!0?a.push({material:s,slot:d,texture:c,level:e}):a.push({material:s,slot:d,texture:null,level:e})}return a})}if(t instanceof y.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,n){return(t==null?void 0:t.isTexture)!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):O.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if((r==null?void 0:r.isTexture)===!0){if(r!=t&&s&&n){const i=s[n];if(i&&!x){const a=this.getAssignedLODInformation(i);if(a&&(a==null?void 0:a.level)<e)return x==="verbose"&&console.warn("Assigned texture level is already higher: ",a.level,e,s,i,r),null}s[n]=r}return r}else x=="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 x&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,r)=>{var i;if(n!=null&&n.extensions){const a=n==null?void 0:n.extensions[W];if(a){if(!a.lods){x&&console.warn("Texture has no LODs",a);return}let l=!1;for(const c of this.parser.associations.keys())if(c.isTexture===!0){const d=this.parser.associations.get(c);(d==null?void 0:d.textures)===r&&(l=!0,O.registerTexture(this.url,c,(i=a.lods)==null?void 0:i.length,r,a))}l||this.parser.getDependency("texture",r).then(c=>{var d;c&&O.registerTexture(this.url,c,(d=a.lods)==null?void 0:d.length,r,a)})}}}),(s=this.parser.json.meshes)==null||s.forEach((n,r)=>{if(n!=null&&n.extensions){const i=n==null?void 0:n.extensions[W];if(i&&i.lods){for(const a of this.parser.associations.keys())if(a.isMesh){const l=this.parser.associations.get(a);(l==null?void 0:l.meshes)===r&&O.registerMesh(this.url,i.guid,a,i.lods.length,l.primitives,i)}}}}),null}static async getOrLoadLOD(t,e){var a,l,c,d;const s=x=="verbose",n=t.userData.LODS;if(!n)return null;const r=n==null?void 0:n.key;let i;if(t.isTexture===!0){const p=t;p.source&&p.source[Me]&&(i=p.source[Me])}if(i||(i=O.lodInfos.get(r)),i){if(e>0){let M=!1;const D=Array.isArray(i.lods);if(D&&e>=i.lods.length?M=!0:D||(M=!0),M)return this.lowresCache.get(r)}const p=Array.isArray(i.lods)?(a=i.lods[e])==null?void 0:a.path:i.lods;if(!p)return x&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const f=rt(n.url,p);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const M=f+"_"+i.guid,D=this.previouslyLoaded.get(M);if(D!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${M}`);let h=await D.catch(G=>(console.error(`Error loading LOD ${e} from ${f}
|
|
2
|
+
`,G),null)),_=!1;if(h==null||(h instanceof y.Texture&&t instanceof y.Texture?(l=h.image)!=null&&l.data||(c=h.source)!=null&&c.data?h=this.copySettings(t,h):(_=!0,this.previouslyLoaded.delete(M)):h instanceof y.BufferGeometry&&t instanceof y.BufferGeometry&&((d=h.attributes.position)!=null&&d.array||(_=!0,this.previouslyLoaded.delete(M)))),!_)return h}const C=await this.queue.slot(f);if(!C.use)return x&&console.log(`LOD ${e} was aborted: ${f}`),null;const S=i,F=new Promise(async(h,_)=>{const G=new Pe.GLTFLoader;Ae(G),x&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+f,S.guid));let V=f;if(S&&Array.isArray(S.lods)){const v=S.lods[e];v.hash&&(V+="?v="+v.hash)}const P=await G.loadAsync(V).catch(v=>(console.error(`Error loading LOD ${e} from ${f}
|
|
3
|
+
`,v),h(null)));if(!P)return h(null);const ee=P.parser;s&&console.log("Loading finished "+f,S.guid);let E=0;if(P.parser.json.textures){let v=!1;for(const m of P.parser.json.textures){if(m!=null&&m.extensions){const g=m==null?void 0:m.extensions[W];if(g!=null&&g.guid&&g.guid===S.guid){v=!0;break}}E++}if(v){let m=await ee.getDependency("texture",E);return m&&O.assignLODInformation(n.url,m,r,e,void 0),s&&console.log('change "'+t.name+'" → "'+m.name+'"',f,E,m,M),t instanceof y.Texture&&(m=this.copySettings(t,m)),m&&(m.guid=S.guid),h(m)}else x&&console.warn("Could not find texture with guid",S.guid,P.parser.json)}if(E=0,P.parser.json.meshes){let v=!1;for(const m of P.parser.json.meshes){if(m!=null&&m.extensions){const g=m==null?void 0:m.extensions[W];if(g!=null&&g.guid&&g.guid===S.guid){v=!0;break}}E++}if(v){const m=await ee.getDependency("mesh",E);if(s&&console.log(`Loaded Mesh "${m.name}"`,f,E,m,M),m.isMesh===!0){const g=m.geometry;return O.assignLODInformation(n.url,g,r,e,0),h(g)}else{const g=new Array;for(let w=0;w<m.children.length;w++){const B=m.children[w];if(B.isMesh===!0){const $=B.geometry;O.assignLODInformation(n.url,$,r,e,w),g.push($)}}return h(g)}}else x&&console.warn("Could not find mesh with guid",S.guid,P.parser.json)}return h(null)});return this.previouslyLoaded.set(M,F),C.use(F),await F}else if(t instanceof y.Texture){s&&console.log("Load texture from uri: "+f);const D=await new y.TextureLoader().loadAsync(f);return D?(D.guid=i.guid,D.flipY=!1,D.needsUpdate=!0,D.colorSpace=t.colorSpace,s&&console.log(i,D)):x&&console.warn("failed loading",f),D}}else x&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,s,n,r){if(!e)return;e.userData||(e.userData={});const i=new ut(t,s,n,r);e.userData.LODS=i}static getAssignedLODInformation(t){var e;return((e=t==null?void 0:t.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e?(x&&console.warn(`Copy texture settings
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}};let P=S;c(P,"registerTexture",(t,e,s,i,r)=>{if(L&&console.log("> Progressive: register texture",i,e.name,e.uuid,e,r),!e){L&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ve]=r);const o=r.guid;S.assignLODInformation(t,e,o,s,i),S.lodInfos.set(o,r),S.lowresCache.set(o,e)}),c(P,"registerMesh",(t,e,s,i,r,o)=>{var u;const a=s.geometry;if(!a){L&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),L&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},o,s),S.assignLODInformation(t,a,e,i,r),S.lodInfos.set(e,o);let l=S.lowresCache.get(e);l?l.push(s.geometry):l=[s.geometry],S.lowresCache.set(e,l),i>0&&!oe(s)&&Ve(s,a);for(const d of Z)(u=d.onRegisteredNewMesh)==null||u.call(d,s,o)}),c(P,"lodInfos",new Map),c(P,"previouslyLoaded",new Map),c(P,"lowresCache",new Map);class lt{constructor(t,e,s,i){c(this,"url");c(this,"key");c(this,"level");c(this,"index");this.url=t,this.key=e,this.level=s,i!=null&&(this.index=i)}}class pe{constructor(t,e){c(this,"frame_start");c(this,"frame_capture_end");c(this,"ready");c(this,"_resolve");c(this,"_signal");c(this,"_resolved",!1);c(this,"_addedCount",0);c(this,"_resolvedCount",0);c(this,"_awaiting",[]);c(this,"_maxPromisesPerObject",1);c(this,"_currentFrame",0);c(this,"_seen",new WeakMap);var r;const i=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+i,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,(r=this._signal)==null||r.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}update(t){var e;this._currentFrame=t,((e=this._signal)!=null&&e.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}add(t,e,s){if(this._resolved){L&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let i=this._seen.get(e);if(i>=this._maxPromisesPerObject){L&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,i+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){var t,e;this._resolved||(e=this._resolve)==null||e.call(this,{awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:((t=this._signal)==null?void 0:t.aborted)??!1})}}c(pe,"addPromise",(t,e,s,i)=>{i.forEach(r=>{r.add(t,e,s)})});const N=ce("debugprogressive"),ct=ce("noprogressive"),Me=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),Q=Symbol("Needle:CurrentLOD"),G={mesh_lod:-1,texture_lod:-1};var R,j,ye,ee,re,Le,H;const A=class{constructor(t,e){c(this,"renderer");c(this,"context");c(this,"projectionScreenMatrix",new p.Matrix4);c(this,"targetTriangleDensity",2e5);c(this,"skinnedMeshAutoUpdateBoundsInterval",30);c(this,"updateInterval","auto");J(this,R,1);c(this,"pause",!1);c(this,"manual",!1);c(this,"_newPromiseGroups",[]);c(this,"_promiseGroupIds",0);c(this,"_lodchangedlisteners",[]);J(this,j,void 0);J(this,ye,new p.Clock);J(this,ee,0);J(this,re,0);J(this,Le,0);J(this,H,0);c(this,"_fpsBuffer",[60,60,60,60,60]);c(this,"_sphere",new p.Sphere);c(this,"_tempBox",new p.Box3);c(this,"_tempBox2",new p.Box3);c(this,"tempMatrix",new p.Matrix4);c(this,"_tempWorldPosition",new p.Vector3);c(this,"_tempBoxSize",new p.Vector3);c(this,"_tempBox2Size",new p.Vector3);this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[De]}static addPlugin(t){Z.push(t)}static removePlugin(t){const e=Z.indexOf(t);e>=0&&Z.splice(e,1)}static get(t,e){if(t[Me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Me];const s=new A(t,{engine:"unknown",...e});return t[Me]=s,s}get plugins(){return Z}awaitLoading(t){const e=this._promiseGroupIds++,s=new pe(m(this,ee),{...t});this._newPromiseGroups.push(s);const i=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),it()&&performance.measure("LODsManager:awaitLoading",{start:i,detail:{id:e,name:t==null?void 0:t.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(m(this,ee))}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(m(this,j))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;W(this,j,this.renderer.render);const e=this;Pe(this.renderer),this.renderer.render=function(s,i){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,W(e,ee,m(e,ee)+1),W(e,re,m(e,ye).getDelta()),W(e,Le,m(e,Le)+m(e,re)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/m(e,re)),W(e,H,e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length),N&&m(e,ee)%200===0&&console.log("FPS",Math.round(m(e,H)),"Interval:",m(e,R)));const o=t++;m(e,j).call(this,s,i),e.onAfterRender(s,i,o)}}disable(){m(this,j)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=m(this,j),W(this,j,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const r=this.renderer.renderLists.get(t,0).opaque;let o=!0;if(r.length===1){const a=r[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(o=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(o=!1),o){if(ct||(this.updateInterval==="auto"?m(this,H)<40&&m(this,R)<10?(W(this,R,m(this,R)+1),N&&console.warn("↓ Reducing LOD updates",m(this,R),m(this,H).toFixed(0))):m(this,H)>=60&&m(this,R)>1&&(W(this,R,m(this,R)-1),N&&console.warn("↑ Increasing LOD updates",m(this,R),m(this,H).toFixed(0))):W(this,R,this.updateInterval),m(this,R)>0&&m(this,ee)%m(this,R)!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){var l,u;const s=this.renderer.renderLists.get(t,0),i=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const d of i){if(d.material&&(((l=d.geometry)==null?void 0:l.type)==="BoxGeometry"||((u=d.geometry)==null?void 0:u.type)==="BufferGeometry")&&(d.material.name==="SphericalGaussianBlur"||d.material.name=="BackgroundCubeMaterial"||d.material.name==="CubemapFromEquirect"||d.material.name==="EquirectangularToCubeUV")){N&&(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",d,d.material.name,d.material.type)));continue}switch(d.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(N==="color"&&d.material&&!d.object.progressive_debug_color){d.object.progressive_debug_color=!0;const g=Math.random()*16777215,w=new p.MeshStandardMaterial({color:g});d.object.material=w}const h=d.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,r)}const o=s.transparent;for(const d of o){const h=d.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,r)}const a=s.transmissive;for(const d of a){const h=d.object;(h instanceof p.Mesh||h.isMesh)&&this.updateLODs(t,e,h,r)}}updateLODs(t,e,s,i){var a,l;s.userData||(s.userData={});let r=s[De];if(r||(r=new dt,s[De]=r),r.frames++<2)return;for(const u of Z)(a=u.onBeforeUpdateLOD)==null||a.call(u,this.renderer,t,e,s);const o=A.overrideGlobalLodLevel!==void 0?A.overrideGlobalLodLevel:te;o>=0?(G.mesh_lod=o,G.texture_lod=o):(this.calculateLodLevel(e,s,r,i,G),G.mesh_lod=Math.round(G.mesh_lod),G.texture_lod=Math.round(G.texture_lod)),G.mesh_lod>=0&&this.loadProgressiveMeshes(s,G.mesh_lod),s.material&&G.texture_lod>=0&&this.loadProgressiveTextures(s.material,G.texture_lod),L&&s.material&&!s.isGizmo&&We(s.material);for(const u of Z)(l=u.onAfterUpdatedLOD)==null||l.call(u,this.renderer,t,e,s,G);r.lastLodLevel_Mesh=G.mesh_lod,r.lastLodLevel_Texture=G.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let s=!1;(t[Q]===void 0||e<t[Q])&&(s=!0);const i=t["DEBUG:LOD"];if(i!=null&&(s=t[Q]!=i,e=i),s){t[Q]=e;const r=P.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(a=>a({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[Q]!==e;const i=t["DEBUG:LOD"];if(i!=null&&(s=t[Q]!=i,e=i),s){t[Q]=e;const r=t.geometry,o=P.assignMeshLOD(t,e).then(a=>(a&&t[Q]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return pe.addPromise("mesh",t,o,this._newPromiseGroups),o}return Promise.resolve(null)}static isInside(t,e){const s=t.min,i=t.max,r=(s.x+i.x)*.5,o=(s.y+i.y)*.5;return this._tempPtInside.set(r,o,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,i,r){var V,K,v;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let a=10+1,l=!1;if(N&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=(V=P.getMeshLODExtension(e.geometry))==null?void 0:V.lods,d=P.getPrimitiveIndex(e.geometry),h=u&&u.length>0,g=P.getMaterialMinMaxLODsCount(e.material),w=(g==null?void 0:g.min_count)!=1/0&&g.min_count>0&&g.max_count>0;if(!h&&!w){r.mesh_lod=0,r.texture_lod=0;return}h||(l=!0,a=0);const O=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let _=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const D=e;if(!D.boundingBox)D.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0&&s.frames%this.skinnedMeshAutoUpdateBoundsInterval===0){const y=oe(D),Y=D.geometry;y&&(D.geometry=y),D.computeBoundingBox(),D.geometry=Y}_=D.boundingBox}if(_){const D=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 x=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(x)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(_),this._tempBox.applyMatrix4(e.matrixWorld),D.isPerspectiveCamera&&A.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&D.isPerspectiveCamera&&D.fov>70){const x=this._tempBox.min,M=this._tempBox.max;let E=x.x,$=x.y,z=M.x,ie=M.y;const de=2,xe=1.5,ue=(x.x+M.x)*.5,fe=(x.y+M.y)*.5;E=(E-ue)*de+ue,$=($-fe)*de+fe,z=(z-ue)*de+ue,ie=(ie-fe)*de+fe;const Ke=E<0&&z>0?0:Math.min(Math.abs(x.x),Math.abs(M.x)),Ye=$<0&&ie>0?0:Math.min(Math.abs(x.y),Math.abs(M.y)),_e=Math.max(Ke,Ye);s.lastCentrality=(xe-_e)*(xe-_e)*(xe-_e)}else s.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&O>0&&y.multiplyScalar(O/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const Y=t.matrixWorldInverse,C=this._tempBox2;C.copy(_),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(Y);const F=C.getSize(this._tempBox2Size),k=Math.max(F.x,F.y);if(Math.max(y.x,y.y)!=0&&k!=0&&(y.z=F.z/Math.max(F.x,F.y)*Math.max(y.x,y.y)),s.lastScreenCoverage=Math.max(y.x,y.y,y.z),s.lastScreenspaceVolume.copy(y),s.lastScreenCoverage*=s.lastCentrality,N&&A.debugDrawLine){const x=this.tempMatrix.copy(this.projectionScreenMatrix);x.invert();const M=A.corner0,E=A.corner1,$=A.corner2,z=A.corner3;M.copy(this._tempBox.min),E.copy(this._tempBox.max),E.x=M.x,$.copy(this._tempBox.max),$.y=M.y,z.copy(this._tempBox.max);const ie=(M.z+z.z)*.5;M.z=E.z=$.z=z.z=ie,M.applyMatrix4(x),E.applyMatrix4(x),$.applyMatrix4(x),z.applyMatrix4(x),A.debugDrawLine(M,E,255),A.debugDrawLine(M,$,255),A.debugDrawLine(E,z,255),A.debugDrawLine($,z,255)}let f=999;if(u&&s.lastScreenCoverage>0)for(let x=0;x<u.length;x++){const M=u[x];if((((K=M.densities)==null?void 0:K[d])||M.density||1e-5)/s.lastScreenCoverage<i){f=x;break}}f<a&&(a=f,l=!0)}if(l?r.mesh_lod=a:r.mesh_lod=s.lastLodLevel_Mesh,N&&r.mesh_lod!=s.lastLodLevel_Mesh){const y=u==null?void 0:u[r.mesh_lod];y&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (${y.density.toFixed(0)}) - ${e.name}`)}if(w){const D="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=g.max_count-1,N){const y=g.lods[g.max_count-1];N&&console.log(`First Texture LOD ${r.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let Y=s.lastScreenCoverage*4;((v=this.context)==null?void 0:v.engine)==="model-viewer"&&(Y*=1.5);const F=O/window.devicePixelRatio*Y;let k=!1;for(let b=g.lods.length-1;b>=0;b--){const f=g.lods[b];if(!(D&&f.max_height>=2048)&&!(ot()&&f.max_height>4096)&&(f.max_height>F||!k&&b===0)){if(k=!0,r.texture_lod=b,r.texture_lod<s.lastLodLevel_Texture){const T=f.max_height;N&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${T}px
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${e.name}`)}break}}}}else r.texture_lod=0}};let
|
|
8
|
-
`,
|
|
5
|
+
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}};let T=O;u(T,"registerTexture",(t,e,s,n,r)=>{if(x&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,r),!e){x&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[Me]=r);const i=r.guid;O.assignLODInformation(t,e,i,s,n),O.lodInfos.set(i,r),O.lowresCache.set(i,e)}),u(T,"registerMesh",(t,e,s,n,r,i)=>{var c;const a=s.geometry;if(!a){x&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),x&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},i,s),O.assignLODInformation(t,a,e,n,r),O.lodInfos.set(e,i);let l=O.lowresCache.get(e);l?l.push(s.geometry):l=[s.geometry],O.lowresCache.set(e,l),n>0&&!ie(s)&&Ve(s,a);for(const d of Q)(c=d.onRegisteredNewMesh)==null||c.call(d,s,i)}),u(T,"lodInfos",new Map),u(T,"previouslyLoaded",new Map),u(T,"lowresCache",new Map),u(T,"queue",new ot(100,{debug:x!=!1}));class ut{constructor(t,e,s,n){u(this,"url");u(this,"key");u(this,"level");u(this,"index");this.url=t,this.key=e,this.level=s,n!=null&&(this.index=n)}}class pe{constructor(t,e){u(this,"frame_start");u(this,"frame_capture_end");u(this,"ready");u(this,"_resolve");u(this,"_signal");u(this,"_resolved",!1);u(this,"_addedCount",0);u(this,"_resolvedCount",0);u(this,"_awaiting",[]);u(this,"_maxPromisesPerObject",1);u(this,"_currentFrame",0);u(this,"_seen",new WeakMap);var r;const n=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+n,this.ready=new Promise(i=>{this._resolve=i}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,(r=this._signal)==null||r.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}update(t){var e;this._currentFrame=t,((e=this._signal)!=null&&e.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}add(t,e,s){if(this._resolved){x&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){x&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,n+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){var t,e;this._resolved||(e=this._resolve)==null||e.call(this,{awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:((t=this._signal)==null?void 0:t.aborted)??!1})}}u(pe,"addPromise",(t,e,s,n)=>{n.forEach(r=>{r.add(t,e,s)})});const N=ue("debugprogressive"),dt=ue("noprogressive"),ve=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),H=Symbol("Needle:CurrentLOD"),k={mesh_lod:-1,texture_lod:-1};var R,K,ye,J,re,Le,Y;const b=class{constructor(t,e){u(this,"renderer");u(this,"context");u(this,"projectionScreenMatrix",new y.Matrix4);u(this,"targetTriangleDensity",2e5);u(this,"skinnedMeshAutoUpdateBoundsInterval",30);u(this,"updateInterval","auto");j(this,R,1);u(this,"pause",!1);u(this,"manual",!1);u(this,"_newPromiseGroups",[]);u(this,"_promiseGroupIds",0);u(this,"_lodchangedlisteners",[]);j(this,K,void 0);j(this,ye,new y.Clock);j(this,J,0);j(this,re,0);j(this,Le,0);j(this,Y,0);u(this,"_fpsBuffer",[60,60,60,60,60]);u(this,"_sphere",new y.Sphere);u(this,"_tempBox",new y.Box3);u(this,"_tempBox2",new y.Box3);u(this,"tempMatrix",new y.Matrix4);u(this,"_tempWorldPosition",new y.Vector3);u(this,"_tempBoxSize",new y.Vector3);u(this,"_tempBox2Size",new y.Vector3);this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[De]}static addPlugin(t){Q.push(t)}static removePlugin(t){const e=Q.indexOf(t);e>=0&&Q.splice(e,1)}static get(t,e){if(t[ve])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ve];const s=new b(t,{engine:"unknown",...e});return t[ve]=s,s}get plugins(){return Q}awaitLoading(t){const e=this._promiseGroupIds++,s=new pe(L(this,J),{...t});this._newPromiseGroups.push(s);const n=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),nt()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t==null?void 0:t.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(L(this,J))}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(L(this,K))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;z(this,K,this.renderer.render);const e=this;Te(this.renderer),this.renderer.render=function(s,n){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,z(e,J,L(e,J)+1),z(e,re,L(e,ye).getDelta()),z(e,Le,L(e,Le)+L(e,re)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/L(e,re)),z(e,Y,e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length),N&&L(e,J)%200===0&&console.log("FPS",Math.round(L(e,Y)),"Interval:",L(e,R)));const i=t++;L(e,K).call(this,s,n),e.onAfterRender(s,n,i)}}disable(){L(this,K)&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=L(this,K),z(this,K,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const r=this.renderer.renderLists.get(t,0).opaque;let i=!0;if(r.length===1){const a=r[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(dt||(this.updateInterval==="auto"?L(this,Y)<40&&L(this,R)<10?(z(this,R,L(this,R)+1),N&&console.warn("↓ Reducing LOD updates",L(this,R),L(this,Y).toFixed(0))):L(this,Y)>=60&&L(this,R)>1&&(z(this,R,L(this,R)-1),N&&console.warn("↑ Increasing LOD updates",L(this,R),L(this,Y).toFixed(0))):z(this,R,this.updateInterval),L(this,R)>0&&L(this,J)%L(this,R)!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){var l,c;const s=this.renderer.renderLists.get(t,0),n=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const d of n){if(d.material&&(((l=d.geometry)==null?void 0:l.type)==="BoxGeometry"||((c=d.geometry)==null?void 0:c.type)==="BufferGeometry")&&(d.material.name==="SphericalGaussianBlur"||d.material.name=="BackgroundCubeMaterial"||d.material.name==="CubemapFromEquirect"||d.material.name==="EquirectangularToCubeUV")){N&&(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(d.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",d,d.material.name,d.material.type)));continue}switch(d.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(N==="color"&&d.material&&!d.object.progressive_debug_color){d.object.progressive_debug_color=!0;const f=Math.random()*16777215,M=new y.MeshStandardMaterial({color:f});d.object.material=M}const p=d.object;(p instanceof y.Mesh||p.isMesh)&&this.updateLODs(t,e,p,r)}const i=s.transparent;for(const d of i){const p=d.object;(p instanceof y.Mesh||p.isMesh)&&this.updateLODs(t,e,p,r)}const a=s.transmissive;for(const d of a){const p=d.object;(p instanceof y.Mesh||p.isMesh)&&this.updateLODs(t,e,p,r)}}updateLODs(t,e,s,n){var a,l;s.userData||(s.userData={});let r=s[De];if(r||(r=new ct,s[De]=r),r.frames++<2)return;for(const c of Q)(a=c.onBeforeUpdateLOD)==null||a.call(c,this.renderer,t,e,s);const i=b.overrideGlobalLodLevel!==void 0?b.overrideGlobalLodLevel:te;i>=0?(k.mesh_lod=i,k.texture_lod=i):(this.calculateLodLevel(e,s,r,n,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),s.material&&k.texture_lod>=0&&this.loadProgressiveTextures(s.material,k.texture_lod),x&&s.material&&!s.isGizmo&&ze(s.material);for(const c of Q)(l=c.onAfterUpdatedLOD)==null||l.call(c,this.renderer,t,e,s,k);r.lastLodLevel_Mesh=k.mesh_lod,r.lastLodLevel_Texture=k.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let s=!1;(t[H]===void 0||e<t[H])&&(s=!0);const n=t["DEBUG:LOD"];if(n!=null&&(s=t[H]!=n,e=n),s){t[H]=e;const r=T.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(a=>a({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[H]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(s=t[H]!=n,e=n),s){t[H]=e;const r=t.geometry,i=T.assignMeshLOD(t,e).then(a=>(a&&t[H]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return pe.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}static isInside(t,e){const s=t.min,n=t.max,r=(s.x+n.x)*.5,i=(s.y+n.y)*.5;return this._tempPtInside.set(r,i,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,n,r){var S,F,Z;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let a=10+1,l=!1;if(N&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const c=(S=T.getMeshLODExtension(e.geometry))==null?void 0:S.lods,d=T.getPrimitiveIndex(e.geometry),p=c&&c.length>0,f=T.getMaterialMinMaxLODsCount(e.material),M=(f==null?void 0:f.min_count)!=1/0&&f.min_count>0&&f.max_count>0;if(!p&&!M){r.mesh_lod=0,r.texture_lod=0;return}p||(l=!0,a=0);const D=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let C=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[b.$skinnedMeshBoundsOffset]){const G=b.skinnedMeshBoundsFrameOffsetCounter++;h[b.$skinnedMeshBoundsOffset]=G}const _=h[b.$skinnedMeshBoundsOffset];if((s.frames+_)%this.skinnedMeshAutoUpdateBoundsInterval===0){const G=ie(h),V=h.geometry;G&&(h.geometry=G),h.computeBoundingBox(),h.geometry=V}}C=h.boundingBox}if(C){const h=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)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(C),this._tempBox.applyMatrix4(e.matrixWorld),h.isPerspectiveCamera&&b.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&h.isPerspectiveCamera&&h.fov>70){const g=this._tempBox.min,w=this._tempBox.max;let B=g.x,$=g.y,q=w.x,ne=w.y;const de=2,xe=1.5,ce=(g.x+w.x)*.5,fe=(g.y+w.y)*.5;B=(B-ce)*de+ce,$=($-fe)*de+fe,q=(q-ce)*de+ce,ne=(ne-fe)*de+fe;const Ke=B<0&&q>0?0:Math.min(Math.abs(g.x),Math.abs(w.x)),Ye=$<0&&ne>0?0:Math.min(Math.abs(g.y),Math.abs(w.y)),_e=Math.max(Ke,Ye);s.lastCentrality=(xe-_e)*(xe-_e)*(xe-_e)}else s.lastCentrality=1;const _=this._tempBox.getSize(this._tempBoxSize);_.multiplyScalar(.5),screen.availHeight>0&&D>0&&_.multiplyScalar(D/screen.availHeight),t.isPerspectiveCamera?_.x*=t.aspect:t.isOrthographicCamera;const G=t.matrixWorldInverse,V=this._tempBox2;V.copy(C),V.applyMatrix4(e.matrixWorld),V.applyMatrix4(G);const P=V.getSize(this._tempBox2Size),ee=Math.max(P.x,P.y);if(Math.max(_.x,_.y)!=0&&ee!=0&&(_.z=P.z/Math.max(P.x,P.y)*Math.max(_.x,_.y)),s.lastScreenCoverage=Math.max(_.x,_.y,_.z),s.lastScreenspaceVolume.copy(_),s.lastScreenCoverage*=s.lastCentrality,N&&b.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const w=b.corner0,B=b.corner1,$=b.corner2,q=b.corner3;w.copy(this._tempBox.min),B.copy(this._tempBox.max),B.x=w.x,$.copy(this._tempBox.max),$.y=w.y,q.copy(this._tempBox.max);const ne=(w.z+q.z)*.5;w.z=B.z=$.z=q.z=ne,w.applyMatrix4(g),B.applyMatrix4(g),$.applyMatrix4(g),q.applyMatrix4(g),b.debugDrawLine(w,B,255),b.debugDrawLine(w,$,255),b.debugDrawLine(B,q,255),b.debugDrawLine($,q,255)}let v=999;if(c&&s.lastScreenCoverage>0)for(let g=0;g<c.length;g++){const w=c[g];if((((F=w.densities)==null?void 0:F[d])||w.density||1e-5)/s.lastScreenCoverage<n){v=g;break}}v<a&&(a=v,l=!0)}if(l?r.mesh_lod=a:r.mesh_lod=s.lastLodLevel_Mesh,N&&r.mesh_lod!=s.lastLodLevel_Mesh){const _=c==null?void 0:c[r.mesh_lod];_&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (${_.density.toFixed(0)}) - ${e.name}`)}if(M){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=f.max_count-1,N){const _=f.lods[f.max_count-1];N&&console.log(`First Texture LOD ${r.texture_lod} (${_.max_height}px) - ${e.name}`)}}else{const _=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let G=s.lastScreenCoverage*4;((Z=this.context)==null?void 0:Z.engine)==="model-viewer"&&(G*=1.5);const P=D/window.devicePixelRatio*G;let ee=!1;for(let E=f.lods.length-1;E>=0;E--){const v=f.lods[E];if(!(h&&v.max_height>=2048)&&!(it()&&v.max_height>4096)&&(v.max_height>P||!ee&&E===0)){if(ee=!0,r.texture_lod=E,r.texture_lod<s.lastLodLevel_Texture){const m=v.max_height;N&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${m}px
|
|
6
|
+
Screensize: ${P.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${_.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else r.texture_lod=0}};let A=b;R=new WeakMap,K=new WeakMap,ye=new WeakMap,J=new WeakMap,re=new WeakMap,Le=new WeakMap,Y=new WeakMap,u(A,"debugDrawLine"),u(A,"overrideGlobalLodLevel"),u(A,"corner0",new y.Vector3),u(A,"corner1",new y.Vector3),u(A,"corner2",new y.Vector3),u(A,"corner3",new y.Vector3),u(A,"_tempPtInside",new y.Vector3),u(A,"skinnedMeshBoundsFrameOffsetCounter",0),u(A,"$skinnedMeshBoundsOffset",Symbol("gltf-progressive-skinnedMeshBoundsOffset"));class ct{constructor(){u(this,"frames",0);u(this,"lastLodLevel_Mesh",-1);u(this,"lastLodLevel_Texture",-1);u(this,"lastScreenCoverage",0);u(this,"lastScreenspaceVolume",new y.Vector3);u(this,"lastCentrality",0)}}const Be=Symbol("NEEDLE_mesh_lod"),he=Symbol("NEEDLE_texture_lod");let le=null;function Ge(){const o=ft();o&&(o.mapURLs(function(t){return $e(),t}),$e(),le==null||le.disconnect(),le=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&We(s)})})}),le.observe(document,{childList:!0,subtree:!0}))}function ft(){if(typeof customElements>"u")return null;const o=customElements.get("model-viewer");return o||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ge()}),null)}function $e(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{We(t)})}const ke=new WeakSet;let ht=0;function We(o){if(!o||ke.has(o))return null;ke.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++ht+`
|
|
8
|
+
`,o.getAttribute("src"));let t=null,e=null,s=null;for(let n=o;n!=null;n=Object.getPrototypeOf(n)){const r=Object.getOwnPropertySymbols(n),i=r.find(c=>c.toString()=="Symbol(renderer)"),a=r.find(c=>c.toString()=="Symbol(scene)"),l=r.find(c=>c.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&a!=null&&(e=o[a]),!s&&l!=null&&(s=o[l])}if(t&&e){let n=function(){if(s){let i=0,a=setInterval(()=>{if(i++>5){clearInterval(a);return}s==null||s.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=A.get(t,{engine:"model-viewer"});return A.addPlugin(new gt),r.enable(),r.addEventListener("changed",()=>{s==null||s.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&(s==null||s.call(o))}),o.addEventListener("load",()=>{n()}),()=>{r.disable()}}return null}class gt{constructor(){u(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(t,e,s,n){this.tryParseMeshLOD(e,n),this.tryParseTextureLOD(e,n)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[he]==!0)return;e[he]=!0;const s=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),r=this.getUrl(n);if(r&&s&&e.material){let i=function(l){var d,p,f;if(l[he]==!0)return;l[he]=!0,l.userData&&(l.userData.LOD=-1);const c=Object.keys(l);for(let M=0;M<c.length;M++){const D=c[M],C=l[D];if((C==null?void 0:C.isTexture)===!0){const S=(p=(d=C.userData)==null?void 0:d.associations)==null?void 0:p.textures;if(S==null)continue;const F=s.parser.json.textures[S];if(!F){console.warn("Texture data not found for texture index "+S);continue}if((f=F==null?void 0:F.extensions)!=null&&f[W]){const Z=F.extensions[W];Z&&r&&T.registerTexture(r,C,Z.lods.length,S,Z)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(t,e){var i,a;if(e[Be]==!0)return;e[Be]=!0;const s=this.tryGetCurrentModelViewer(t),n=this.getUrl(s);if(!n)return;const r=(a=(i=e.userData)==null?void 0:i.gltfExtensions)==null?void 0:a[W];if(r&&n){const l=e.uuid;T.registerMesh(n,l,e,0,r.lods.length,r)}}}function Xe(o,t,e,s){Te(t),Ae(e),Ce(e,{progressive:!0,...s==null?void 0:s.hints}),e.register(r=>new T(r,o));const n=A.get(t);return(s==null?void 0:s.enableLODsManager)!==!1&&n.enable(),n}Ge();if(!at){const o={gltfProgressive:{useNeedleProgressive:Xe,LODsManager:A,configureLoader:Ce,getRaycastMesh:ie,useRaycastMeshes:qe}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=W;exports.LODsManager=A;exports.NEEDLE_progressive=T;exports.VERSION=Re;exports.addDracoAndKTX2Loaders=Ae;exports.configureLoader=Ce;exports.createLoaders=Te;exports.getRaycastMesh=ie;exports.patchModelViewer=Ge;exports.registerRaycastMesh=Ve;exports.setDracoDecoderLocation=Fe;exports.setKTX2TranscoderLocation=Ne;exports.useNeedleProgressive=Xe;exports.useRaycastMeshes=qe;
|
package/lib/extension.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
123
123
|
/** this contains the geometry/textures that were originally loaded */
|
|
124
124
|
private static readonly lowresCache;
|
|
125
125
|
private static getOrLoadLOD;
|
|
126
|
+
private static queue;
|
|
126
127
|
private static assignLODInformation;
|
|
127
128
|
private static getAssignedLODInformation;
|
|
128
129
|
private static copySettings;
|
package/lib/extension.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BufferGeometry, Mesh, Texture, TextureLoader } from "three";
|
|
2
2
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
3
|
import { addDracoAndKTX2Loaders } from "./loaders.js";
|
|
4
|
-
import { resolveUrl } from "./utils.internal.js";
|
|
4
|
+
import { PromiseQueue, resolveUrl } from "./utils.internal.js";
|
|
5
5
|
import { getRaycastMesh, registerRaycastMesh } from "./utils.js";
|
|
6
6
|
// All of this has to be removed
|
|
7
7
|
// import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
|
|
@@ -499,20 +499,20 @@ export class NEEDLE_progressive {
|
|
|
499
499
|
return null;
|
|
500
500
|
}
|
|
501
501
|
const LODKEY = LOD?.key;
|
|
502
|
-
let
|
|
502
|
+
let lodInfo;
|
|
503
503
|
// See https://github.com/needle-tools/needle-engine-support/issues/133
|
|
504
504
|
if (current.isTexture === true) {
|
|
505
505
|
const tex = current;
|
|
506
506
|
if (tex.source && tex.source[$progressiveTextureExtension])
|
|
507
|
-
|
|
507
|
+
lodInfo = tex.source[$progressiveTextureExtension];
|
|
508
508
|
}
|
|
509
|
-
if (!
|
|
510
|
-
|
|
511
|
-
if (
|
|
509
|
+
if (!lodInfo)
|
|
510
|
+
lodInfo = NEEDLE_progressive.lodInfos.get(LODKEY);
|
|
511
|
+
if (lodInfo) {
|
|
512
512
|
if (level > 0) {
|
|
513
513
|
let useLowRes = false;
|
|
514
|
-
const hasMultipleLevels = Array.isArray(
|
|
515
|
-
if (hasMultipleLevels && level >=
|
|
514
|
+
const hasMultipleLevels = Array.isArray(lodInfo.lods);
|
|
515
|
+
if (hasMultipleLevels && level >= lodInfo.lods.length) {
|
|
516
516
|
useLowRes = true;
|
|
517
517
|
}
|
|
518
518
|
else if (!hasMultipleLevels) {
|
|
@@ -524,12 +524,12 @@ export class NEEDLE_progressive {
|
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
/** the unresolved LOD url */
|
|
527
|
-
const unresolved_lod_url = Array.isArray(
|
|
527
|
+
const unresolved_lod_url = Array.isArray(lodInfo.lods) ? lodInfo.lods[level]?.path : lodInfo.lods;
|
|
528
528
|
// check if we have a uri
|
|
529
529
|
if (!unresolved_lod_url) {
|
|
530
|
-
if (debug && !
|
|
531
|
-
|
|
532
|
-
console.warn("Missing uri for progressive asset for LOD " + level,
|
|
530
|
+
if (debug && !lodInfo["missing:uri"]) {
|
|
531
|
+
lodInfo["missing:uri"] = true;
|
|
532
|
+
console.warn("Missing uri for progressive asset for LOD " + level, lodInfo);
|
|
533
533
|
}
|
|
534
534
|
return null;
|
|
535
535
|
}
|
|
@@ -537,12 +537,12 @@ export class NEEDLE_progressive {
|
|
|
537
537
|
const lod_url = resolveUrl(LOD.url, unresolved_lod_url);
|
|
538
538
|
// check if the requested file needs to be loaded via a GLTFLoader
|
|
539
539
|
if (lod_url.endsWith(".glb") || lod_url.endsWith(".gltf")) {
|
|
540
|
-
if (!
|
|
541
|
-
console.warn("missing pointer for glb/gltf texture",
|
|
540
|
+
if (!lodInfo.guid) {
|
|
541
|
+
console.warn("missing pointer for glb/gltf texture", lodInfo);
|
|
542
542
|
return null;
|
|
543
543
|
}
|
|
544
544
|
// check if the requested file has already been loaded
|
|
545
|
-
const KEY = lod_url + "_" +
|
|
545
|
+
const KEY = lod_url + "_" + lodInfo.guid;
|
|
546
546
|
// check if the requested file is currently being loaded
|
|
547
547
|
const existing = this.previouslyLoaded.get(KEY);
|
|
548
548
|
if (existing !== undefined) {
|
|
@@ -581,7 +581,13 @@ export class NEEDLE_progressive {
|
|
|
581
581
|
return res;
|
|
582
582
|
}
|
|
583
583
|
}
|
|
584
|
-
const
|
|
584
|
+
const slot = await this.queue.slot(lod_url);
|
|
585
|
+
if (!slot.use) {
|
|
586
|
+
if (debug)
|
|
587
|
+
console.log(`LOD ${level} was aborted: ${lod_url}`);
|
|
588
|
+
return null; // the request was aborted, we don't load it again
|
|
589
|
+
}
|
|
590
|
+
const ext = lodInfo;
|
|
585
591
|
const request = new Promise(async (resolve, _) => {
|
|
586
592
|
const loader = new GLTFLoader();
|
|
587
593
|
addDracoAndKTX2Loaders(loader);
|
|
@@ -599,10 +605,11 @@ export class NEEDLE_progressive {
|
|
|
599
605
|
}
|
|
600
606
|
const gltf = await loader.loadAsync(url).catch(err => {
|
|
601
607
|
console.error(`Error loading LOD ${level} from ${lod_url}\n`, err);
|
|
602
|
-
return null;
|
|
608
|
+
return resolve(null);
|
|
603
609
|
});
|
|
604
|
-
if (!gltf)
|
|
605
|
-
return null;
|
|
610
|
+
if (!gltf) {
|
|
611
|
+
return resolve(null);
|
|
612
|
+
}
|
|
606
613
|
const parser = gltf.parser;
|
|
607
614
|
if (debugverbose)
|
|
608
615
|
console.log("Loading finished " + lod_url, ext.guid);
|
|
@@ -687,6 +694,7 @@ export class NEEDLE_progressive {
|
|
|
687
694
|
return resolve(null);
|
|
688
695
|
});
|
|
689
696
|
this.previouslyLoaded.set(KEY, request);
|
|
697
|
+
slot.use(request);
|
|
690
698
|
const res = await request;
|
|
691
699
|
return res;
|
|
692
700
|
}
|
|
@@ -697,12 +705,12 @@ export class NEEDLE_progressive {
|
|
|
697
705
|
const loader = new TextureLoader();
|
|
698
706
|
const tex = await loader.loadAsync(lod_url);
|
|
699
707
|
if (tex) {
|
|
700
|
-
tex.guid =
|
|
708
|
+
tex.guid = lodInfo.guid;
|
|
701
709
|
tex.flipY = false;
|
|
702
710
|
tex.needsUpdate = true;
|
|
703
711
|
tex.colorSpace = current.colorSpace;
|
|
704
712
|
if (debugverbose)
|
|
705
|
-
console.log(
|
|
713
|
+
console.log(lodInfo, tex);
|
|
706
714
|
}
|
|
707
715
|
else if (debug)
|
|
708
716
|
console.warn("failed loading", lod_url);
|
|
@@ -716,6 +724,7 @@ export class NEEDLE_progressive {
|
|
|
716
724
|
}
|
|
717
725
|
return null;
|
|
718
726
|
}
|
|
727
|
+
static queue = new PromiseQueue(100, { debug: debug != false });
|
|
719
728
|
static assignLODInformation(url, res, key, level, index) {
|
|
720
729
|
if (!res)
|
|
721
730
|
return;
|
package/lib/lods.manager.d.ts
CHANGED
|
@@ -149,6 +149,8 @@ export declare class LODsManager {
|
|
|
149
149
|
private static corner3;
|
|
150
150
|
private static readonly _tempPtInside;
|
|
151
151
|
private static isInside;
|
|
152
|
+
private static skinnedMeshBoundsFrameOffsetCounter;
|
|
153
|
+
private static $skinnedMeshBoundsOffset;
|
|
152
154
|
private calculateLodLevel;
|
|
153
155
|
}
|
|
154
156
|
declare class LOD_state {
|
package/lib/lods.manager.js
CHANGED
|
@@ -457,6 +457,8 @@ export class LODsManager {
|
|
|
457
457
|
const pt1 = this._tempPtInside.set(centerx, centery, min.z).applyMatrix4(matrix);
|
|
458
458
|
return pt1.z < 0;
|
|
459
459
|
}
|
|
460
|
+
static skinnedMeshBoundsFrameOffsetCounter = 0;
|
|
461
|
+
static $skinnedMeshBoundsOffset = Symbol("gltf-progressive-skinnedMeshBoundsOffset");
|
|
460
462
|
calculateLodLevel(camera, mesh, state, desiredDensity, result) {
|
|
461
463
|
if (!mesh) {
|
|
462
464
|
result.mesh_lod = -1;
|
|
@@ -502,16 +504,25 @@ export class LODsManager {
|
|
|
502
504
|
skinnedMesh.computeBoundingBox();
|
|
503
505
|
}
|
|
504
506
|
// Fix: https://linear.app/needle/issue/NE-5264
|
|
505
|
-
else if (this.skinnedMeshAutoUpdateBoundsInterval > 0
|
|
506
|
-
|
|
507
|
-
//
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
skinnedMesh
|
|
507
|
+
else if (this.skinnedMeshAutoUpdateBoundsInterval > 0) {
|
|
508
|
+
// Save a frame offset per object to stagger updates of skinned meshes across multiple frames
|
|
509
|
+
// This isn't a perfect solution to improve perf impact of skinned mesh updates (e.g. large skinned meshes would still be costly)
|
|
510
|
+
// But for many smaller meshes it helps to avoid spikes in performance
|
|
511
|
+
if (!skinnedMesh[LODsManager.$skinnedMeshBoundsOffset]) {
|
|
512
|
+
const offset = LODsManager.skinnedMeshBoundsFrameOffsetCounter++;
|
|
513
|
+
skinnedMesh[LODsManager.$skinnedMeshBoundsOffset] = offset;
|
|
514
|
+
}
|
|
515
|
+
const frameOffset = skinnedMesh[LODsManager.$skinnedMeshBoundsOffset];
|
|
516
|
+
if ((state.frames + frameOffset) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
|
|
517
|
+
// use lowres geometry for bounding box calculation
|
|
518
|
+
const raycastmesh = getRaycastMesh(skinnedMesh);
|
|
519
|
+
const originalGeometry = skinnedMesh.geometry;
|
|
520
|
+
if (raycastmesh) {
|
|
521
|
+
skinnedMesh.geometry = raycastmesh;
|
|
522
|
+
}
|
|
523
|
+
skinnedMesh.computeBoundingBox();
|
|
524
|
+
skinnedMesh.geometry = originalGeometry;
|
|
512
525
|
}
|
|
513
|
-
skinnedMesh.computeBoundingBox();
|
|
514
|
-
skinnedMesh.geometry = originalGeometry;
|
|
515
526
|
}
|
|
516
527
|
boundingBox = skinnedMesh.boundingBox;
|
|
517
528
|
}
|