@needle-tools/gltf-progressive 3.6.0-alpha.1 → 3.6.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/gltf-progressive.js +593 -527
- package/gltf-progressive.min.js +9 -9
- package/gltf-progressive.umd.cjs +9 -9
- package/lib/extension.d.ts +10 -1
- package/lib/extension.js +90 -14
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/lods.manager.d.ts +25 -12
- package/lib/lods.manager.js +149 -134
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{BufferGeometry as F,Mesh as q,Box3 as ie,Vector3 as k,Sphere as be,CompressedTexture as qe,Texture as $,Matrix3 as ze,InterleavedBuffer as Ve,InterleavedBufferAttribute as Xe,BufferAttribute as He,TextureLoader as Ke,Color as Me,Matrix4 as De,Clock as Ye}from"three";import{GLTFLoader as ae}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Qe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Je}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as Ze}from"three/examples/jsm/loaders/KTX2Loader.js";const Te="";globalThis.GLTF_PROGRESSIVE_VERSION=Te,console.debug("[gltf-progressive] version -");let A="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const et=A,tt=z,Oe=new URL(A+"draco_decoder.js");Oe.searchParams.append("range","true"),fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${A} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),A===et&&Se("./include/draco/"),z===tt&&ke("./include/ktx2/")}).finally(()=>{Ce()});const rt=()=>({dracoDecoderPath:A,ktx2TranscoderPath:z});function Se(i){A=i,T&&T[ue]!=A?(console.debug("Updating Draco decoder path to "+i),T[ue]=A,T.setDecoderPath(A),T.preload()):console.debug("Setting Draco decoder path to "+i)}function ke(i){z=i,R&&R.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),R.setTranscoderPath(z),R.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Ce(),i?R.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:R,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(R),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let T,te,R;function Ce(){T||(T=new Je,T[ue]=A,T.setDecoderPath(A),T.setDecoderConfig({type:"js"}),T.preload()),R||(R=new Ze,R.setTranscoderPath(z),R.init()),te||(te=Qe)}const ce=new WeakMap;function de(i,t){let e=ce.get(i);e?e=Object.assign(e,t):e=t,ce.set(i,e)}const st=ae.prototype.load;function ot(...i){const t=ce.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return i[0]=e,st?.call(this,...i)}ae.prototype.load=ot,N("debugprogressive");function N(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function nt(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function he(){return re!==void 0||(re=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",re)),re}let re;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class it{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};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 finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}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:r,resolve:s}=this._queue.shift();s({use:o=>this.add(r,o)})}}}function at(i){const t=i.image?.width??0,e=i.image?.height??0,r=i.image?.depth??1,s=Math.floor(Math.log2(Math.max(t,e,r)))+1,o=lt(i);return t*e*r*o*(1-Math.pow(.25,s))/(1-.25)}function lt(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let r=1;const s=i.type;return s===1009||s===1010?r=1:s===1011||s===1012?r=2:s===1013||s===1014||s===1015?r=4:s===1016&&(r=2),t*r}const ut=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function X(i){return i?.[ge]instanceof F?i[ge]:null}function Ae(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!X(i)){const e=ct(t);e.userData={isRaycastMesh:!0},i[ge]=e}}function Re(i=!0){if(i){if(H)return;const t=H=q.prototype.raycast;q.prototype.raycast=function(e,r){const s=this,o=X(s);let n;o&&s.isMesh&&(n=s.geometry,s.geometry=o),t.call(this,e,r),n&&(s.geometry=n)}}else{if(!H)return;q.prototype.raycast=H,H=null}}let H=null;function ct(i){const t=new F;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,g=N("debugprogressive");let K,V=-1;if(g){let i=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(K=!K,console.log(`Toggle wireframe [${K}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(V=r,console.log(`Set LOD level to [${V}]`))})}function Ee(i){if(g&&K!==void 0)if(Array.isArray(i))for(const t of i)Ee(t);else i&&"wireframe"in i&&(i.wireframe=K===!0)}const Y=new Array;let dt=0;const ht=he()?2:10;function gt(i){if(Y.length<ht){const e=Y.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=_e.createWorker(i||{});return Y.push(r),r}const t=dt++%Y.length;return Y[t]}class _e{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const s=r.data;switch(this._debug&&console.log("[Worker] EVENT",s),s.type){case"loaded-gltf":for(const o of this._running)if(o.url===s.result.url){ft(s.result),o.resolve(s.result);const n=o.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new _e(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=rt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),s=await this._webglRenderer);const o=ee(s).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const n={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:o};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ft(i){for(const t of i.geometries){const e=t.geometry,r=new F;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(fe(s))}for(const s in e.attributes){const o=e.attributes[s],n=fe(o);r.setAttribute(s,n)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(n=>fe(n));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ie,r.boundingBox.min=new k(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new k(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new be(new k(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const s of e.groups)r.addGroup(s.start,s.count,s.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const s=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new qe(s,o,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new $(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new ze(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return i}function fe(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,s=new Ve(r,e.stride);t=new Xe(s,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new He(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const mt=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const Q=N("gltf-progressive-gc"),me=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class m{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const r=this,s="LODS:minmax",o=t[s];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const u of t)this.getMaterialMinMaxLODsCount(u,e);return t[s]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const u=t;for(const a of Object.keys(u.uniforms)){const l=u.uniforms[a].value;l?.isTexture===!0&&n(l,e)}}else if(t.isMaterial)for(const u of Object.keys(t)){const a=t[u];a?.isTexture===!0&&n(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function n(u,a){const l=r.getAssignedLODInformation(u);if(l){const c=r.lodInfos.get(l.key);if(c&&c.lods){a.min_count=Math.min(a.min_count,c.lods.length),a.max_count=Math.max(a.max_count,c.lods.length);for(let h=0;h<c.lods.length;h++){const v=c.lods[h];v.width&&(a.lods[h]=a.lods[h]||{min_height:1/0,max_height:0},a.lods[h].min_height=Math.min(a.lods[h].min_height,v.height),a.lods[h].max_height=Math.max(a.lods[h].max_height,v.height))}}}}}static hasLODLevelAvailable(t,e){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 n=t[o];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,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 r,s;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const o=r.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof q||t.isMesh===!0){const r=t.geometry,s=this.getAssignedLODInformation(r);if(!s)return Promise.resolve(null);for(const o of W)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(r,e).then(o=>{if(Array.isArray(o)){const n=s.index||0;o=o[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],o&&r!=o&&(o?.isBufferGeometry?t.geometry=o:g&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else g&&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,r){if(!t)return Promise.resolve(null);const s=r?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const n=new Array;for(const u of o.material){const a=this.assignTextureLOD(u,e,r);n.push(a)}return Promise.all(n).then(u=>{const a=new Array;for(const l of u)Array.isArray(l)&&a.push(...l);return a})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,n=[],u=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const a=o;for(const l of Object.keys(a.uniforms)){const c=a.uniforms[l].value;if(c?.isTexture===!0){const h=this.assignTextureLODForSlot(c,e,o,l,s).then(v=>(v&&a.uniforms[l].value!=v&&(a.uniforms[l].value=v,a.uniformsNeedUpdate=!0),v));n.push(h),u.push(l)}}}else for(const a of Object.keys(o)){const l=o[a];if(l?.isTexture===!0){const c=this.assignTextureLODForSlot(l,e,o,a,s);n.push(c),u.push(a)}}return Promise.all(n).then(a=>{const l=new Array;for(let c=0;c<a.length;c++){const h=a[c],v=u[c];h&&h.isTexture===!0?l.push({material:o,slot:v,texture:h,level:e}):l.push({material:o,slot:v,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,s)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,r,s,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(s==="glyphMap")return Promise.resolve(t);const n=this.getAssignedLODInformation(t);if(n&&(n.level===e||!o&&n.level<e))return Promise.resolve(t);if(r&&s){const a=this.getPendingTextureSlotRequest(r,s);if(a&&a.level===e&&a.force===o)return a.promise}const u=m.getOrLoadLOD(t,e).then(a=>{if(Array.isArray(a))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(a?.isTexture===!0){if(a!=t&&r&&s){const l=this.getMaterialTextureSlot(r,s)??t;if(l&&!o){const c=this.getAssignedLODInformation(l);if(c&&c?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",c.level,e,r,l,a),null}this.assignTrackedTextureSlot(r,s,a)}return a}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(a=>(console.error("Error loading LOD",t,a),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,u),u}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const r of Object.keys(e.uniforms)){const s=e.uniforms[r].value;s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,r,s)}return}for(const e of Object.keys(t)){const r=t[e];r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,r)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static setPendingTextureSlotRequest(t,e,r,s,o){let n=this.pendingTextureSlotRequests.get(t);n||(n=new Map,this.pendingTextureSlotRequests.set(t,n));const u={level:r,force:s,promise:o};n.set(e,u),o.finally(()=>{n.get(e)===u&&n.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const s=t[e];return s?.isTexture===!0?s:null}static setMaterialTextureSlot(t,e,r){const s=t.uniforms?.[e];if(s?.value?.isTexture===!0){s.value=r,t.uniformsNeedUpdate=!0;return}t[e]=r}static assignTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=this.getMaterialTextureSlot(t,e);let n=s.get(e);!n&&o?n=this.ensureTrackedTextureSlot(t,e,o):n&&o&&n!==o&&(this.releaseTrackedTextureSlot(t,e,n),n=this.ensureTrackedTextureSlot(t,e,o)),!(n===r&&o===r)&&(n&&n!==r&&this.releaseTrackedTextureSlot(t,e,n),n!==r&&(this.trackTextureUsage(r),s.set(e,r)),o!==r&&this.setMaterialTextureSlot(t,e,r))}static ensureTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=s.get(e);return o===r?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(r),s.set(e,r),r)}static releaseTrackedTextureSlot(t,e,r){const s=this.trackedTextureSlots.get(t);if(s?.get(e)===r&&s.delete(e),this.untrackTextureUsage(r)&&(g||Q)){const o=this.getAssignedLODInformation(r);console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level??"?"} for ${t.name||t.type}.${e}`,r.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&m.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[B];if(s){if(!s.lods){g&&console.warn("Texture has no LODs",s);return}let o=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(o=!0,m.registerTexture(this.url,n,s.lods?.length,r,s));o||this.parser.getDependency("texture",r).then(n=>{n&&m.registerTexture(this.url,n,s.lods?.length,r,s)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[B];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const n=this.parser.associations.get(o);n?.meshes===r&&m.registerMesh(this.url,s.guid,o,s.lods.length,n.primitives,s)}}}}),null}static registerTexture=(t,e,r,s,o)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const u=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${u}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[me]=o);const n=o.guid;m.assignLODInformation(t,e,n,r,s),m.lodInfos.set(n,o),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,r,s,o,n)=>{const u=r.geometry;if(!u){g&&console.warn("gltf-progressive: Register mesh without geometry");return}u.userData||(u.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},n,r),m.assignLODInformation(t,u,e,s,o),m.lodInfos.set(e,n);let a=m.lowresCache.get(e)?.deref();a?a.push(r.geometry):a=[r.geometry],m.lowresCache.set(e,new WeakRef(a)),s>0&&!X(r)&&Ae(r,u);for(const l of W)l.onRegisteredNewMesh?.(r,n)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}this.lowresCache.delete(t)}for(const[r,s]of this.cache)r.includes(t)&&(this._disposeCacheEntry(s),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const r of e)r.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(g||Q)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(
|
|
3
|
-
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const
|
|
4
|
-
`,
|
|
5
|
-
`,
|
|
1
|
+
import{BufferGeometry as V,Mesh as X,Box3 as oe,Vector3 as S,Sphere as Se,CompressedTexture as Ye,Texture as I,Matrix3 as Qe,InterleavedBuffer as Je,InterleavedBufferAttribute as Ze,BufferAttribute as et,TextureLoader as tt,Color as Oe,Matrix4 as ke,Clock as rt}from"three";import{GLTFLoader as de}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as st}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as ot}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as nt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ce="";globalThis.GLTF_PROGRESSIVE_VERSION=Ce,console.debug("[gltf-progressive] version -");let P="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",H="https://cdn.needle.tools/static/three/0.179.1/basis2/";const it=P,at=H,Pe=new URL(P+"draco_decoder.js");Pe.searchParams.append("range","true"),fetch(Pe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${P} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),P===it&&Ae("./include/draco/"),H===at&&Re("./include/ktx2/")}).finally(()=>{Ee()});const lt=()=>({dracoDecoderPath:P,ktx2TranscoderPath:H});function Ae(n){P=n,T&&T[ge]!=P?(console.debug("Updating Draco decoder path to "+n),T[ge]=P,T.setDecoderPath(P),T.preload()):console.debug("Setting Draco decoder path to "+n)}function Re(n){H=n,A&&A.transcoderPath!=H?(console.debug("Updating KTX2 transcoder path to "+n),A.setTranscoderPath(H),A.init()):console.debug("Setting KTX2 transcoder path to "+n)}function ne(n){return Ee(),n?A.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:A,meshoptDecoder:ie}}function he(n){n.dracoLoader||n.setDRACOLoader(T),n.ktx2Loader||n.setKTX2Loader(A),n.meshoptDecoder||n.setMeshoptDecoder(ie)}const ge=Symbol("dracoDecoderPath");let T,ie,A;function Ee(){T||(T=new ot,T[ge]=P,T.setDecoderPath(P),T.setDecoderConfig({type:"js"}),T.preload()),A||(A=new nt,A.setTranscoderPath(H),A.init()),ie||(ie=st)}const fe=new WeakMap;function me(n,t){let e=fe.get(n);e?e=Object.assign(e,t):e=t,fe.set(n,e)}const ut=de.prototype.load;function ct(...n){const t=fe.get(this);let e=n[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return n[0]=e,ut?.call(this,...n)}de.prototype.load=ct,N("debugprogressive");function N(n){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(n);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function dt(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function pe(){return ae!==void 0||(ae=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ae)),ae}let ae;function Ie(){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}class ht{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};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 finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}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:r,resolve:s}=this._queue.shift();s({use:o=>this.add(r,o)})}}}function gt(n){const t=n.image?.width??0,e=n.image?.height??0,r=n.image?.depth??1,s=Math.floor(Math.log2(Math.max(t,e,r)))+1,o=ft(n);return t*e*r*o*(1-Math.pow(.25,s))/(1-.25)}function ft(n){let t=4;const e=n.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let r=1;const s=n.type;return s===1009||s===1010?r=1:s===1011||s===1012?r=2:s===1013||s===1014||s===1015?r=4:s===1016&&(r=2),t*r}const mt=typeof window>"u"&&typeof document>"u",xe=Symbol("needle:raycast-mesh");function Y(n){return n?.[xe]instanceof V?n[xe]:null}function $e(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=pt(t);e.userData={isRaycastMesh:!0},n[xe]=e}}function Be(n=!0){if(n){if(ee)return;const t=ee=X.prototype.raycast;X.prototype.raycast=function(e,r){const s=this,o=Y(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),t.call(this,e,r),i&&(s.geometry=i)}}else{if(!ee)return;X.prototype.raycast=ee,ee=null}}let ee=null;function pt(n){const t=new V;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const W=new Array,g=N("debugprogressive");let te,K=-1;if(g){let n=function(){K+=1,K>=t&&(K=-1),console.log(`Toggle LOD level [${K}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(K=r,console.log(`Set LOD level to [${K}]`))})}function Ge(n){if(g&&te!==void 0)if(Array.isArray(n))for(const t of n)Ge(t);else n&&"wireframe"in n&&(n.wireframe=te===!0)}const re=new Array;let xt=0;const yt=pe()?2:10;function vt(n){if(re.length<yt){const e=re.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Te.createWorker(n||{});return re.push(r),r}const t=xt++%re.length;return re[t]}class Te{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const s=r.data;switch(this._debug&&console.log("[Worker] EVENT",s),s.type){case"loaded-gltf":for(const o of this._running)if(o.url===s.result.url){wt(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Te(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=lt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:a}=await import("three");return new a})(),s=await this._webglRenderer);const o=ne(s).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const i={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:o};return this._debug&&console.debug("[Worker] Sending load request",i),this.worker.postMessage(i),new Promise(a=>{this._running.push({url:t.toString(),resolve:a})})}_debug=!1}function wt(n){for(const t of n.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(ye(s))}for(const s in e.attributes){const o=e.attributes[s],i=ye(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>ye(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new oe,r.boundingBox.min=new S(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new S(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Se(new S(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const s of e.groups)r.addGroup(s.start,s.count,s.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of n.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const s=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;r=new Ye(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new I(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new Qe(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return n}function ye(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new Je(r,e.stride);t=new Ze(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new et(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const Lt=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const se=N("gltf-progressive-gc"),ve=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class f{get name(){return $}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const r=this,s="LODS:minmax",o=t[s];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[s]=e,e}if(g==="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?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];l?.isTexture===!0&&i(l,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(a,l){const u=r.getAssignedLODInformation(a);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let d=0;d<c.lods.length;d++){const p=c.lods[d];p.width&&(l.lods[d]=l.lods[d]||{min_height:1/0,max_height:0},l.lods[d].min_height=Math.min(l.lods[d].min_height,p.height),l.lods[d].max_height=Math.max(l.lods[d].max_height,p.height))}}}}}static hasLODLevelAvailable(t,e){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 r,s;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const o=r.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e,r){if(!t)return Promise.resolve(null);if(t instanceof X||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const a of W)a.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return f.getOrLoadLOD(s,e,{isCurrent:i}).then(a=>{if(Array.isArray(a)){const u=o.index||0;a=a[u]}const l=t["LOD:requested level"]===e;return(l||this.shouldApplyStaleMeshLOD(t,e))&&(l&&delete t["LOD:requested level"],a&&s!=a&&(a?.isBufferGeometry?typeof r?.apply=="function"?r.apply(a,e,t):r?.apply!==!1&&(t.geometry=a):g&&console.error("Invalid LOD geometry",a))),a}).catch(a=>(console.error("Error loading mesh LOD",t,a),null))}else g&&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,r){if(!t)return Promise.resolve(null);const s=r?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const i=new Array;for(const a of o.material){const l=this.assignTextureLOD(a,e,r);i.push(l)}return Promise.all(i).then(a=>{const l=new Array;for(const u of a)Array.isArray(u)&&l.push(...u);return l})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],a=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const l=o;for(const u of Object.keys(l.uniforms)){const c=l.uniforms[u].value;if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,u,s).then(p=>(p&&l.uniforms[u].value!=p&&(l.uniforms[u].value=p,l.uniformsNeedUpdate=!0),p));i.push(d),a.push(u)}}}else for(const l of Object.keys(o)){const u=o[l];if(u?.isTexture===!0){const c=this.assignTextureLODForSlot(u,e,o,l,s);i.push(c),a.push(l)}}return Promise.all(i).then(l=>{const u=new Array;for(let c=0;c<l.length;c++){const d=l[c],p=a[c];d&&d.isTexture===!0?u.push({material:o,slot:p,texture:d,level:e}):u.push({material:o,slot:p,texture:null,level:e})}return u})}if(t instanceof I||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,s)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,r,s,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(s==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!o&&i.level<e))return Promise.resolve(t);if(r&&s){const d=this.getPendingTextureSlotRequest(r,s);if(d&&d.level===e&&d.force===o)return d.promise}const a=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,l=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===a,u=()=>l()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),c=f.getOrLoadLOD(t,e,{isCurrent:u}).then(d=>{if(!l()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(d))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(d?.isTexture===!0){if(d!=t&&r&&s){const p=this.getMaterialTextureSlot(r,s)??t;if(p&&!o){const m=this.getAssignedLODInformation(p);if(m&&m?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",m.level,e,r,p,d),null}this.assignTrackedTextureSlot(r,s,d)}return d}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(d=>(console.error("Error loading LOD",t,d),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,a,c),c}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const r of Object.keys(e.uniforms)){const s=e.uniforms[r].value;s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,r,s)}return}for(const e of Object.keys(t)){const r=t[e];r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,r)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,r,s){let o=this.latestTextureSlotRequests.get(t);o||(o=new Map,this.latestTextureSlotRequests.set(t,o));const i=++this.textureSlotRequestId;return o.set(e,{id:i,level:r,force:s}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,r,s){if(!t||!e)return!1;const o=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),a=this.getAssignedLODInformation(i)?.level??1/0;return r>=a?!1:s?o?r>=o.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const r=t["LOD:requested level"];if(typeof r!="number")return!1;const s=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<s&&e>=r}static setPendingTextureSlotRequest(t,e,r,s,o,i){let a=this.pendingTextureSlotRequests.get(t);a||(a=new Map,this.pendingTextureSlotRequests.set(t,a));const l={level:r,force:s,id:o,promise:i};a.set(e,l),i.finally(()=>{a.get(e)?.id===o&&a.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const s=t[e];return s?.isTexture===!0?s:null}static setMaterialTextureSlot(t,e,r){const s=t.uniforms?.[e];if(s?.value?.isTexture===!0){s.value=r,t.uniformsNeedUpdate=!0;return}t[e]=r}static assignTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=this.getMaterialTextureSlot(t,e);let i=s.get(e);!i&&o?i=this.ensureTrackedTextureSlot(t,e,o):i&&o&&i!==o&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,o)),!(i===r&&o===r)&&(i&&i!==r&&this.releaseTrackedTextureSlot(t,e,i),i!==r&&(this.trackTextureUsage(r),s.set(e,r)),o!==r&&this.setMaterialTextureSlot(t,e,r))}static ensureTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=s.get(e);return o===r?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(r),s.set(e,r),r)}static releaseTrackedTextureSlot(t,e,r){const s=this.trackedTextureSlots.get(t);if(s?.get(e)===r&&s.delete(e),this.untrackTextureUsage(r)&&(g||se)){const o=this.getAssignedLODInformation(r);console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level??"?"} for ${t.name||t.type}.${e}`,r.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&f.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s){if(!s.lods){g&&console.warn("Texture has no LODs",s);return}let o=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(o=!0,f.registerTexture(this.url,i,s.lods?.length,r,s));o||this.parser.getDependency("texture",r).then(i=>{i&&f.registerTexture(this.url,i,s.lods?.length,r,s)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const i=this.parser.associations.get(o);i?.meshes===r&&f.registerMesh(this.url,s.guid,o,s.lods.length,i.primitives,s)}}}}),null}static registerTexture=(t,e,r,s,o)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const a=e.image?.width||e.source?.data?.width||0,l=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${a}x${l}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[ve]=o);const i=o.guid;f.assignLODInformation(t,e,i,r,s),f.lodInfos.set(i,o),f.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,r,s,o,i)=>{const a=r.geometry;if(!a){g&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),f.assignLODInformation(t,a,e,s,o),f.lodInfos.set(e,i);let l=f.lowresCache.get(e)?.deref();l?l.push(r.geometry):l=[r.geometry],f.lowresCache.set(e,new WeakRef(l)),s>0&&!Y(r)&&$e(r,a);for(const u of W)u.onRegisteredNewMesh?.(r,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}this.lowresCache.delete(t)}for(const[r,s]of this.cache)r.includes(t)&&(this._disposeCacheEntry(s),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const r of e)r.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(g||se)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(g||se)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,r+1),g==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${r} \u2192 ${r+1}`)}static untrackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e);if(!r)return(g==="verbose"||se)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const s=r-1;if(s<=0)return this.textureRefCounts.delete(e),(g||se)&&o("[gltf-progressive] Memory: Dispose texture",s),t.dispose(),!0;return this.textureRefCounts.set(e,s),g==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",s),!1;function o(i,a){const l=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0,c=l&&u?`${l}x${u}`:"N/A";let d="N/A";l&&u&&(d=`~${(gt(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} \u2014 ${t.name} ${c} (${d}), refCount: ${r} \u2192 ${a}
|
|
3
|
+
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,r){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const i=o?.key;let a;if(t.isTexture===!0){const l=t;l.source&&l.source[ve]&&(a=l.source[ve])}if(a||(a=f.lodInfos.get(i)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let c=!1;const d=Array.isArray(a.lods);if(d&&e>=a.lods.length?c=!0:d||(c=!0),c){const p=this.lowresCache.get(i);if(p){const m=p.deref();if(m)return m;this.lowresCache.delete(i),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${i}`)}return null}}const l=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!l)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const u=dt(o.url,l);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const c=u+"_"+a.guid,d=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(d.found)return d.value;if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request before queue: ${u}`),null;const p=await this.queue.slot(u);if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request after queue: ${u}`),null;const m=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(m.found)return m.value;if(!p.use)return g&&console.log(`LOD ${e} was aborted: ${u}`),null;const L=a,h=new Promise(async(D,q)=>{if(Lt){const b=await(await vt({})).load(u);if(b.textures.length>0)for(const x of b.textures){let w=x.texture;return f.assignLODInformation(o.url,w,i,e,void 0),t instanceof I&&(w=this.copySettings(t,w)),w&&(w.guid=L.guid),D(w)}if(b.geometries.length>0){const x=new Array;for(const w of b.geometries){const C=w.geometry;f.assignLODInformation(o.url,C,i,e,w.primitiveIndex),x.push(C)}return D(x)}return D(null)}const B=new de;he(B),g&&(await new Promise(b=>setTimeout(b,1e3)),s&&console.warn("Start loading (delayed) "+u,L.guid));let y=u;if(L&&Array.isArray(L.lods)){const b=L.lods[e];b.hash&&(y+="?v="+b.hash)}const v=await B.loadAsync(y).catch(b=>(console.error(`Error loading LOD ${e} from ${u}
|
|
4
|
+
`,b),D(null)));if(!v)return D(null);const E=v.parser;s&&console.log("Loading finished "+u,L.guid);let k=0;if(v.parser.json.textures){let b=!1;for(const x of v.parser.json.textures){if(x?.extensions){const w=x?.extensions[$];if(w?.guid&&w.guid===L.guid){b=!0;break}}k++}if(b){let x=await E.getDependency("texture",k);return x&&f.assignLODInformation(o.url,x,i,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+x.name+'"',u,k,x,c),t instanceof I&&(x=this.copySettings(t,x)),x&&(x.guid=L.guid),D(x)}else g&&console.warn("Could not find texture with guid",L.guid,v.parser.json)}if(k=0,v.parser.json.meshes){let b=!1;for(const x of v.parser.json.meshes){if(x?.extensions){const w=x?.extensions[$];if(w?.guid&&w.guid===L.guid){b=!0;break}}k++}if(b){const x=await E.getDependency("mesh",k);if(s&&console.log(`Loaded Mesh "${x.name}"`,u,k,x,c),x.isMesh===!0){const w=x.geometry;return f.assignLODInformation(o.url,w,i,e,0),D(w)}else{const w=new Array;for(let C=0;C<x.children.length;C++){const F=x.children[C];if(F.isMesh===!0){const z=F.geometry;f.assignLODInformation(o.url,z,i,e,C),w.push(z)}}return D(w)}}else g&&console.warn("Could not find mesh with guid",L.guid,v.parser.json)}return D(null)});this.cache.set(c,h),p.use(h);const M=await h;return M!=null?M instanceof I?(this.cache.set(c,new WeakRef(M)),f._resourceRegistry.register(M,c)):Array.isArray(M)?this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(null)),M}else if(t instanceof I){if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale texture LOD ${e} request: ${u}`),null;s&&console.log("Load texture from uri: "+u);const c=await new tt().loadAsync(u);return r?.isCurrent?.()===!1?(c?.dispose(),null):(c?(c.guid=a.guid,c.flipY=!1,c.needsUpdate=!0,c.colorSpace=t.colorSpace,s&&console.log(a,c)):g&&console.warn("failed loading",u),c)}}return null}static async tryResolveLODCacheEntry(t,e,r,s,o,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${o} was already loading/loaded: ${e}`),t instanceof WeakRef){const u=t.deref();if(u){let c=u,d=!1;if(c instanceof I&&s instanceof I?c.image?.data||c.source?.data?c=this.copySettings(s,c):d=!0:c instanceof V&&s instanceof V&&(c.attributes.position?.array||(d=!0)),!d)return{found:!0,value:c}}return this.cache.delete(e),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let a=await t.catch(u=>(console.error(`Error loading LOD ${o} from ${r}
|
|
5
|
+
`,u),null)),l=!1;return a==null||(a instanceof I&&s instanceof I?a.image?.data||a.source?.data?a=this.copySettings(s,a):(l=!0,this.cache.delete(e)):a instanceof V&&s instanceof V&&(a.attributes.position?.array||(l=!0,this.cache.delete(e)))),l?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new ht(pe()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,r,s,o){if(!e)return;e.userData||(e.userData={});const i=new bt(t,r,s,o);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
|
|
6
6
|
`,t.uuid,`
|
|
7
|
-
`,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}}class pt{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class pe{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=N("debugprogressive"),xt=E==="colors",yt=N("noprogressive"),xe=Symbol("Needle:LODSManager"),ye=Symbol("Needle:LODState"),j=Symbol("Needle:CurrentLOD"),D={mesh_lod:-1,texture_lod:-1},wt=new Me,we=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];class L{static debugDrawLine;static getObjectLODState(t){return t[ye]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[xe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[xe];const r=new L(t,{engine:"unknown",...e});return t[xe]=r,r}renderer;context;projectionScreenMatrix=new De;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new pe(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Ye;#s=0;#o=0;#i=0;#r=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ee(this.renderer),this.renderer.render=function(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((u,a)=>u+a)/e._fpsBuffer.length,E&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.renderer.renderLists.get(t,0).opaque;let o=!0;if(s.length===1){const n=s[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(o=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(o=!1),o){if(yt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of s){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){E&&(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}const l=a.object;(l instanceof q||l.isMesh)&&this.updateLODs(t,e,l,o)}const n=r.transparent;for(const a of n){const l=a.object;(l instanceof q||l.isMesh)&&this.updateLODs(t,e,l,o)}const u=r.transmissive;for(const a of u){const l=a.object;(l instanceof q||l.isMesh)&&this.updateLODs(t,e,l,o)}}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[ye];if(o||(o=new vt,r[ye]=o),o.frames++<2)return;for(const u of W)u.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;n>=0?(D.mesh_lod=n,D.texture_lod=n):(this.calculateLodLevel(e,r,o,s,D),D.mesh_lod=Math.round(D.mesh_lod),D.texture_lod=Math.round(D.texture_lod)),D.mesh_lod>=0&&this.loadProgressiveMeshes(r,D.mesh_lod),r.material&&D.texture_lod>=0&&this.loadProgressiveTextures(r.material,D.texture_lod,n),g&&r.material&&!r.isGizmo&&Ee(r.material),xt&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&Ie(r.material,D.mesh_lod);for(const u of W)u.onAfterUpdatedLOD?.(this.renderer,t,e,r,D);o.lastLodLevel_Mesh=D.mesh_lod,o.lastLodLevel_Texture=D.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e,r);return}let s=!1;(t[j]===void 0||e<t[j])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[j]!=r,e=r),s){t[j]=e;const n=o?{force:!0}:void 0,u=m.assignTextureLOD(t,e,n).then(a=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,u,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[j]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[j]!=s,e=s),r){t[j]=e;const o=t.geometry,n=m.assignMeshLOD(t,e).then(u=>(u&&t[j]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),u));return pe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new be;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new De;_tempWorldPosition=new k;_tempBoxSize=new k;_tempBox2Size=new k;static corner0=new k;static corner1=new k;static corner2=new k;static corner3=new k;static _tempPtInside=new k;static isInside(t,e){const r=t.min,s=t.max,o=(r.x+s.x)*.5,n=(r.y+s.y)*.5;return this._tempPtInside.set(o,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let n=11,u=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=m.getMeshLODExtension(e.geometry)?.lods,l=m.getPrimitiveIndex(e.geometry),c=a&&a.length>0,h=m.getMaterialMinMaxLODsCount(e.material),v=h.min_count!==1/0&&h.min_count>=0&&h.max_count>=0;if(!c&&!v){o.mesh_lod=0,o.texture_lod=0;return}c||(u=!0,n=0);const _=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let O=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const p=e;if(!p.boundingBox)p.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!p[L.$skinnedMeshBoundsOffset]){const I=L.skinnedMeshBoundsFrameOffsetCounter++;p[L.$skinnedMeshBoundsOffset]=I}const w=p[L.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const I=X(p),S=p.geometry;I&&(p.geometry=I),p.computeBoundingBox(),p.geometry=S}}O=p.boundingBox}if(O){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 y=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(y)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(O),this._tempBox.applyMatrix4(e.matrixWorld),p.isPerspectiveCamera&&L.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&p.isPerspectiveCamera&&p.fov>70){const y=this._tempBox.min,f=this._tempBox.max;let d=y.x,x=y.y,b=f.x,G=f.y;const U=2,oe=1.5,J=(y.x+f.x)*.5,Z=(y.y+f.y)*.5;d=(d-J)*U+J,x=(x-Z)*U+Z,b=(b-J)*U+J,G=(G-Z)*U+Z;const Ue=d<0&&b>0?0:Math.min(Math.abs(y.x),Math.abs(f.x)),Fe=x<0&&G>0?0:Math.min(Math.abs(y.y),Math.abs(f.y)),ne=Math.max(Ue,Fe);r.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&_>0&&w.multiplyScalar(_/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const I=t.matrixWorldInverse,S=this._tempBox2;S.copy(O),S.applyMatrix4(e.matrixWorld),S.applyMatrix4(I);const C=S.getSize(this._tempBox2Size),M=Math.max(C.x,C.y);if(Math.max(w.x,w.y)!=0&&M!=0&&(w.z=C.z/Math.max(C.x,C.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,E&&L.debugDrawLine){const y=this.tempMatrix.copy(this.projectionScreenMatrix);y.invert();const f=L.corner0,d=L.corner1,x=L.corner2,b=L.corner3;f.copy(this._tempBox.min),d.copy(this._tempBox.max),d.x=f.x,x.copy(this._tempBox.max),x.y=f.y,b.copy(this._tempBox.max);const G=(f.z+b.z)*.5;f.z=d.z=x.z=b.z=G,f.applyMatrix4(y),d.applyMatrix4(y),x.applyMatrix4(y),b.applyMatrix4(y),L.debugDrawLine(f,d,255),L.debugDrawLine(f,x,255),L.debugDrawLine(d,b,255),L.debugDrawLine(x,b,255)}let P=999;if(a&&r.lastScreenCoverage>0)for(let y=0;y<a.length;y++){const f=a[y],d=(f.densities?.[l]||f.density||1e-5)/r.lastScreenCoverage;if(l>0&&Pe()&&!f.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),d<s){P=y;break}}P<n&&(n=P,u=!0)}if(u?o.mesh_lod=n:o.mesh_lod=r.lastLodLevel_Mesh,E&&o.mesh_lod!=r.lastLodLevel_Mesh){const p=a?.[o.mesh_lod];p&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${p.densities?.[l].toFixed(0)}) | ${e.name}`)}if(v){const p="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=h.max_count-1,E){const w=h.lods[h.max_count-1];E&&console.log(`First Texture LOD ${o.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let I=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(I*=1.5);const S=_/window.devicePixelRatio*I;let C=!1;for(let M=h.lods.length-1;M>=0;M--){const P=h.lods[M];if(!(p&&P.max_height>=2048)&&!(he()&&P.max_height>4096)&&(P.max_height>S||!C&&M===0)){if(C=!0,o.texture_lod=M,E&&o.texture_lod<r.lastLodLevel_Texture){const y=P.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${y}px
|
|
8
|
-
Screensize: ${
|
|
9
|
-
${e.name}`)}break}}}}else o.texture_lod=0}}class
|
|
10
|
-
`,
|
|
7
|
+
`,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}}class bt{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class le{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const R=N("debugprogressive"),Mt=R==="colors",Dt=N("noprogressive"),we=Symbol("Needle:LODSManager"),Le=Symbol("Needle:LODState"),j=Symbol("Needle:CurrentLOD"),_={mesh_lod:-1,texture_lod:-1},_t=new Oe,be=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405],Me=new oe,U=new oe,qe=new oe,Tt=new S,St=new S,Ot=new ke,G=new S,Q=new S,J=new S,Z=new S;function kt(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return G.set(s,o,e.z).applyMatrix4(t).z<0}function Ne(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:c=!1}=n,d=f.getMeshLODExtension(t)?.lods,p=f.getPrimitiveIndex(t),m=n.target??{level:a,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new S,centrality:1};if(m.level=a,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1,!d?.length)return m;let L=n.boundingBox??t.boundingBox;if(L||(t.computeBoundingBox(),L=t.boundingBox),!L)return m;if(Me.copy(L).applyMatrix4(e),r.isPerspectiveCamera&&kt(Me,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(U.copy(Me).applyMatrix4(s),l&&r.isPerspectiveCamera&&r.fov>70){const y=U.min,v=U.max;let E=y.x,k=y.y,b=v.x,x=v.y;const w=2,C=1.5,F=(y.x+v.x)*.5,z=(y.y+v.y)*.5;E=(E-F)*w+F,k=(k-z)*w+z,b=(b-F)*w+F,x=(x-z)*w+z;const He=E<0&&b>0?0:Math.min(Math.abs(y.x),Math.abs(v.x)),Ke=k<0&&x>0?0:Math.min(Math.abs(y.y),Math.abs(v.y)),ce=Math.max(He,Ke);m.centrality=(C-ce)*(C-ce)*(C-ce)}const h=U.getSize(Tt);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),qe.copy(L).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const M=qe.getSize(St),D=Math.max(h.x,h.y),q=Math.max(M.x,M.y);D!==0&&q!==0&&(h.z=M.z/q*D);const B=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=B,m.screenspaceVolume.copy(h),B<=0)return m;if(u){const y=Ot.copy(s);y.invert(),G.copy(U.min),Q.copy(U.max),Q.x=G.x,J.copy(U.max),J.y=G.y,Z.copy(U.max);const v=(G.z+Z.z)*.5;G.z=Q.z=J.z=Z.z=v,G.applyMatrix4(y),Q.applyMatrix4(y),J.applyMatrix4(y),Z.applyMatrix4(y),u(G,Q,255),u(G,J,255),u(Q,Z,255),u(J,Z,255)}for(let y=0;y<d.length;y++){const v=d[y],E=v.densities?.[p]||v.density||1e-5;if(p>0&&c&&Ie()&&!v.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),E/B<o){m.level=y;break}}return m}class O{static debugDrawLine;static getObjectLODState(t){return t[Le]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[we])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[we];const r=new O(t,{engine:"unknown",...e});return t[we]=r,r}renderer;context;projectionScreenMatrix=new ke;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),Ie()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return le.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new rt;#s=0;#o=0;#i=0;#r=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ne(this.renderer),this.renderer.render=function(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,R&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.renderer.renderLists.get(t,0).opaque;let o=!0;if(s.length===1){const i=s[0].material;(i.name==="EffectMaterial"||i.name==="CopyShader")&&(o=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(o=!1),o){if(Dt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,R&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,R&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const l of s){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){R&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const i=r.transparent;for(const l of i){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const a=r.transmissive;for(const l of a){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[Le];if(o||(o=new Ct,r[Le]=o),o.frames++<2)return;for(const a of W)a.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:K;i>=0?(_.mesh_lod=i,_.texture_lod=i):(this.calculateLodLevel(e,r,o,s,_),_.mesh_lod=Math.round(_.mesh_lod),_.texture_lod=Math.round(_.texture_lod)),_.mesh_lod>=0&&this.loadProgressiveMeshes(r,_.mesh_lod),r.material&&_.texture_lod>=0&&this.loadProgressiveTextures(r.material,_.texture_lod,i),g&&r.material&&!r.isGizmo&&Ge(r.material),Mt&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&We(r.material,_.mesh_lod);for(const a of W)a.onAfterUpdatedLOD?.(this.renderer,t,e,r,_);o.lastLodLevel_Mesh=_.mesh_lod,o.lastLodLevel_Texture=_.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[j]===void 0||e<t[j])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[j]!=r,e=r),s){t[j]=e;const i=o?{force:!0}:void 0,a=f.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});le.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[j]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[j]!=s,e=s),r){t[j]=e;const o=t.geometry,i=f.assignMeshLOD(t,e).then(a=>(a&&t[j]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new Se;_tempWorldPosition=new S;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=11,a=!1;if(R&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),c=l&&l.length>0,d=f.getMaterialMinMaxLODsCount(e.material),p=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!p){o.mesh_lod=0,o.texture_lod=0;return}c||(a=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let L=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[O.$skinnedMeshBoundsOffset]){const D=O.skinnedMeshBoundsFrameOffsetCounter++;h[O.$skinnedMeshBoundsOffset]=D}const M=h[O.$skinnedMeshBoundsOffset];if((r.frames+M)%this.skinnedMeshAutoUpdateBoundsInterval===0){const D=Y(h),q=h.geometry;D&&(h.geometry=D),h.computeBoundingBox(),h.geometry=q}}L=h.boundingBox}if(L){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const M=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(M)){o.mesh_lod=0,o.texture_lod=0;return}}const h=Ne({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:L,xrEnabled:this.renderer.xr.enabled,debugDrawLine:R?O.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,a=!0)}if(a?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,R&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=l?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[u].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=d.max_count-1,R){const M=d.lods[d.max_count-1];R&&console.log(`First Texture LOD ${o.texture_lod} (${M.max_height}px) - ${e.name}`)}}else{const M=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let D=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(D*=1.5);const q=m/window.devicePixelRatio*D;let B=!1;for(let y=d.lods.length-1;y>=0;y--){const v=d.lods[y];if(!(h&&v.max_height>=2048)&&!(pe()&&v.max_height>4096)&&(v.max_height>q||!B&&y===0)){if(B=!0,o.texture_lod=y,R&&o.texture_lod<r.lastLodLevel_Texture){const E=v.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${E}px
|
|
8
|
+
Screensize: ${q.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${M.toFixed(1)}
|
|
9
|
+
${e.name}`)}break}}}}else o.texture_lod=0}}class Ct{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new S;lastCentrality=0}function We(n,t){if(!(t<0)){if(Array.isArray(n)){for(const e of n)We(e,t);return}"color"in n&&n.color instanceof Oe&&(n.color.copy(je(t,_t)),n.needsUpdate=!0)}}function je(n,t){const e=Math.max(0,Math.min(be.length-1,Math.floor(n)));return t.setHex(be[e])}const Ue=Symbol("NEEDLE_mesh_lod"),ue=Symbol("NEEDLE_texture_lod");let De=null;function _e(){const n=Pt();n&&(n.mapURLs(function(t){return Fe(),t}),Fe(),De?.disconnect(),De=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Ve(r)})})}),De.observe(document,{childList:!0,subtree:!0}))}function Pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),_e()}),null)}function Fe(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(n=>{Ve(n)})}const ze=new WeakSet;let At=0;function Ve(n){if(!n||ze.has(n))return null;ze.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++At+`
|
|
10
|
+
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(u=>u.toString()=="Symbol(renderer)"),a=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&a!=null&&(e=n[a]),!r&&l!=null&&(r=n[l])}if(t&&e){let s=function(){if(r){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=O.get(t,{engine:"model-viewer"});return O.addPlugin(new Rt),o.enable(),o.addEventListener("changed",()=>{r?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(n)}),n.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class Rt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,s){this.tryParseMeshLOD(e,s),this.tryParseTextureLOD(e,s)}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[ue]==!0)return;e[ue]=!0;const r=this.tryGetCurrentGLTF(t),s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(o&&r&&e.material){let i=function(l){if(l[ue]==!0)return;l[ue]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let c=0;c<u.length;c++){const d=u[c],p=l[d];if(p?.isTexture===!0){const m=p.userData?.associations?.textures;if(m==null)continue;const L=r.parser.json.textures[m];if(!L){console.warn("Texture data not found for texture index "+m);continue}if(L?.extensions?.[$]){const h=L.extensions[$];h&&o&&f.registerTexture(o,p,h.lods.length,m,h)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(t,e){if(e[Ue]==!0)return;e[Ue]=!0;const r=this.tryGetCurrentModelViewer(t),s=this.getUrl(r);if(!s)return;const o=e.userData?.gltfExtensions?.[$];if(o&&s){const i=e.uuid;f.registerMesh(s,i,e,0,o.lods.length,o)}}}function Xe(...n){let t,e,r,s;switch(n.length){case 2:[r,e]=n,s={};break;case 3:[r,e,s]=n;break;case 4:[t,e,r,s]=n;break;default:throw new Error("Invalid arguments")}ne(e),he(r),me(r,{progressive:!0,...s?.hints}),r.register(i=>new f(i));const o=O.get(e);return s?.enableLODsManager!==!1&&o.enable(),o}if(_e(),!mt){const n={gltfProgressive:{useNeedleProgressive:Xe,LODsManager:O,configureLoader:me,getRaycastMesh:Y,useRaycastMeshes:Be}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}export{$ as EXTENSION_NAME,O as LODsManager,f as NEEDLE_progressive,Ce as VERSION,he as addDracoAndKTX2Loaders,Ne as calculateMeshLODLevel,me as configureLoader,ne as createLoaders,je as getLODColor,Y as getRaycastMesh,be as lodDebugColors,_e as patchModelViewer,$e as registerRaycastMesh,Ae as setDracoDecoderLocation,Re as setKTX2TranscoderLocation,Xe as useNeedleProgressive,Be as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var ze=Object.create;var Me=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var Ve=Object.getOwnPropertyNames;var Ee=Object.getPrototypeOf,Xe=Object.prototype.hasOwnProperty;var je=(n,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Ve(t))!Xe.call(n,r)&&r!==e&&Me(n,r,{get:()=>t[r],enumerable:!(s=Ne(t,r))||s.enumerable});return n};var Ke=(n,t,e)=>(e=n!=null?ze(Ee(n)):{},je(t||!n||!n.__esModule?Me(e,"default",{value:n,enumerable:!0}):e,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("three"),ye=require("three/examples/jsm/loaders/GLTFLoader.js"),Ye=require("three/examples/jsm/libs/meshopt_decoder.module.js"),He=require("three/examples/jsm/loaders/DRACOLoader.js"),Qe=require("three/examples/jsm/loaders/KTX2Loader.js");var ie=typeof document<"u"?document.currentScript:null;const Oe="";globalThis.GLTF_PROGRESSIVE_VERSION=Oe;console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",V="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Je=I,Ze=V,Ce=new URL(I+"draco_decoder.js");Ce.searchParams.append("range","true");fetch(Ce,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===Je&&Pe("./include/draco/"),V===Ze&&ke("./include/ktx2/")}).finally(()=>{Re()});const et=()=>({dracoDecoderPath:I,ktx2TranscoderPath:V});function Pe(n){I=n,P&&P[he]!=I?(console.debug("Updating Draco decoder path to "+n),P[he]=I,P.setDecoderPath(I),P.preload()):console.debug("Setting Draco decoder path to "+n)}function ke(n){V=n,B&&B.transcoderPath!=V?(console.debug("Updating KTX2 transcoder path to "+n),B.setTranscoderPath(V),B.init()):console.debug("Setting KTX2 transcoder path to "+n)}function re(n){return Re(),n?B.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:B,meshoptDecoder:se}}function xe(n){n.dracoLoader||n.setDRACOLoader(P),n.ktx2Loader||n.setKTX2Loader(B),n.meshoptDecoder||n.setMeshoptDecoder(se)}const he=Symbol("dracoDecoderPath");let P,se,B;function Re(){P||(P=new He.DRACOLoader,P[he]=I,P.setDecoderPath(I),P.setDecoderConfig({type:"js"}),P.preload()),B||(B=new Qe.KTX2Loader,B.setTranscoderPath(V),B.init()),se||(se=Ye.MeshoptDecoder)}const ge=new WeakMap;function we(n,t){let e=ge.get(n);e?e=Object.assign(e,t):e=t,ge.set(n,e)}const tt=ye.GLTFLoader.prototype.load;function st(...n){const t=ge.get(this);let e=n[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=s.toString()}return n[0]=e,tt?.call(this,...n)}ye.GLTFLoader.prototype.load=st;z("debugprogressive");function z(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||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))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}function Le(){return j!==void 0||(j=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",j)),j}let j;function Te(){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}class ot{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};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 finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}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:r}=this._queue.shift();r({use:o=>this.add(s,o)})}}}function nt(n){const t=n.image?.width??0,e=n.image?.height??0,s=n.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,o=it(n);return t*e*s*o*(1-Math.pow(.25,r))/(1-.25)}function it(n){let t=4;const e=n.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=n.type;return r===1009||r===1010?s=1:r===1011||r===1012?s=2:r===1013||r===1014||r===1015?s=4:r===1016&&(s=2),t*s}const at=typeof window>"u"&&typeof document>"u",pe=Symbol("needle:raycast-mesh");function E(n){return n?.[pe]instanceof d.BufferGeometry?n[pe]:null}function Ae(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!E(n)){const s=lt(t);s.userData={isRaycastMesh:!0},n[pe]=s}}function Ie(n=!0){if(n){if(K)return;const t=K=d.Mesh.prototype.raycast;d.Mesh.prototype.raycast=function(e,s){const r=this,o=E(r);let i;o&&r.isMesh&&(i=r.geometry,r.geometry=o),t.call(this,e,s),i&&(r.geometry=i)}}else{if(!K)return;d.Mesh.prototype.raycast=K,K=null}}let K=null;function lt(n){const t=new d.BufferGeometry;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const q=new Array,g=z("debugprogressive");let Q,N=-1;if(g){let n=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(N=s,console.log(`Set LOD level to [${N}]`))})}function Be(n){if(g&&Q!==void 0)if(Array.isArray(n))for(const t of n)Be(t);else n&&"wireframe"in n&&(n.wireframe=Q===!0)}const Y=new Array;let ut=0;const ct=Le()?2:10;function dt(n){if(Y.length<ct){const s=Y.length;g&&console.warn(`[Worker] Creating new worker #${s}`);const r=_e.createWorker(n||{});return Y.push(r),r}const t=ut++%Y.length;return Y[t]}class _e{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const o of this._running)if(o.url===r.result.url){ft(r.result),o.resolve(r.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ie&&ie.tagName.toUpperCase()==="SCRIPT"&&ie.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new _e(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=et();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),r=await this._webglRenderer);const u=re(r).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:u};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function ft(n){for(const t of n.geometries){const e=t.geometry,s=new d.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ae(r))}for(const r in e.attributes){const o=e.attributes[r],i=ae(o);s.setAttribute(r,i)}if(e.morphAttributes)for(const r in e.morphAttributes){const i=e.morphAttributes[r].map(u=>ae(u));s.morphAttributes[r]=i}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new d.Box3,s.boundingBox.min=new d.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new d.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new d.Sphere(new d.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of n.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,o=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;s=new d.CompressedTexture(r,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new d.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new d.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return n}function ae(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,s=e.array,r=new d.InterleavedBuffer(s,e.stride);t=new d.InterleavedBufferAttribute(r,n.itemSize,s.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new d.BufferAttribute(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const ht=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const H=z("gltf-progressive-gc"),le=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class m{get name(){return F}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",o=t[r];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const u of t)this.getMaterialMinMaxLODsCount(u,e);return t[r]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const u=t;for(const a of Object.keys(u.uniforms)){const l=u.uniforms[a].value;l?.isTexture===!0&&i(l,e)}}else if(t.isMaterial)for(const u of Object.keys(t)){const a=t[u];a?.isTexture===!0&&i(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function i(u,a){const l=s.getAssignedLODInformation(u);if(l){const c=s.lodInfos.get(l.key);if(c&&c.lods){a.min_count=Math.min(a.min_count,c.lods.length),a.max_count=Math.max(a.max_count,c.lods.length);for(let h=0;h<c.lods.length;h++){const x=c.lods[h];x.width&&(a.lods[h]=a.lods[h]||{min_height:1/0,max_height:0},a.lods[h].min_height=Math.min(a.lods[h].min_height,x.height),a.lods[h].max_height=Math.max(a.lods[h].max_height,x.height))}}}}}static hasLODLevelAvailable(t,e){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 s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const o=s.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){if(!t)return Promise.resolve(null);if(t instanceof d.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const o of q)o.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,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&&s!=o&&(o?.isBufferGeometry?t.geometry=o:g&&console.error("Invalid LOD geometry",o))),o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else g&&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,s){if(!t)return Promise.resolve(null);const r=s?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const i=new Array;for(const u of o.material){const a=this.assignTextureLOD(u,e,s);i.push(a)}return Promise.all(i).then(u=>{const a=new Array;for(const l of u)Array.isArray(l)&&a.push(...l);return a})}else return this.assignTextureLOD(o.material,e,s)}if(t.isMaterial===!0){const o=t,i=[],u=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const a=o;for(const l of Object.keys(a.uniforms)){const c=a.uniforms[l].value;if(c?.isTexture===!0){const h=this.assignTextureLODForSlot(c,e,o,l,r).then(x=>(x&&a.uniforms[l].value!=x&&(a.uniforms[l].value=x,a.uniformsNeedUpdate=!0),x));i.push(h),u.push(l)}}}else for(const a of Object.keys(o)){const l=o[a];if(l?.isTexture===!0){const c=this.assignTextureLODForSlot(l,e,o,a,r);i.push(c),u.push(a)}}return Promise.all(i).then(a=>{const l=new Array;for(let c=0;c<a.length;c++){const h=a[c],x=u[c];h&&h.isTexture===!0?l.push({material:o,slot:x,texture:h,level:e}):l.push({material:o,slot:x,texture:null,level:e})}return l})}if(t instanceof d.Texture||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,r)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(r==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!o&&i.level<e))return Promise.resolve(t);if(s&&r){const a=this.getPendingTextureSlotRequest(s,r);if(a&&a.level===e&&a.force===o)return a.promise}const u=m.getOrLoadLOD(t,e).then(a=>{if(Array.isArray(a))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(a?.isTexture===!0){if(a!=t&&s&&r){const l=this.getMaterialTextureSlot(s,r)??t;if(l&&!o){const c=this.getAssignedLODInformation(l);if(c&&c?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",c.level,e,s,l,a),null}this.assignTrackedTextureSlot(s,r,a)}return a}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(a=>(console.error("Error loading LOD",t,a),null));return s&&r&&this.setPendingTextureSlotRequest(s,r,e,o,u),u}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const s of Object.keys(e.uniforms)){const r=e.uniforms[s].value;r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,s,r)}return}for(const e of Object.keys(t)){const s=t[e];s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,s)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static setPendingTextureSlotRequest(t,e,s,r,o){let i=this.pendingTextureSlotRequests.get(t);i||(i=new Map,this.pendingTextureSlotRequests.set(t,i));const u={level:s,force:r,promise:o};i.set(e,u),o.finally(()=>{i.get(e)===u&&i.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const o=t[e];return o?.isTexture===!0?o:null}static setMaterialTextureSlot(t,e,s){const o=t.uniforms?.[e];if(o?.value?.isTexture===!0){o.value=s,t.uniformsNeedUpdate=!0;return}t[e]=s}static assignTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const o=this.getMaterialTextureSlot(t,e);let i=r.get(e);!i&&o?i=this.ensureTrackedTextureSlot(t,e,o):i&&o&&i!==o&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,o)),!(i===s&&o===s)&&(i&&i!==s&&this.releaseTrackedTextureSlot(t,e,i),i!==s&&(this.trackTextureUsage(s),r.set(e,s)),o!==s&&this.setMaterialTextureSlot(t,e,s))}static ensureTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const o=r.get(e);return o===s?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(s),r.set(e,s),s)}static releaseTrackedTextureSlot(t,e,s){const r=this.trackedTextureSlots.get(t);if(r?.get(e)===s&&r.delete(e),this.untrackTextureUsage(s)&&(g||H)){const i=this.getAssignedLODInformation(s);console.log(`[gltf-progressive] Disposed old texture LOD ${i?.level??"?"} for ${t.name||t.type}.${e}`,s.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r){if(!r.lods){g&&console.warn("Texture has no LODs",r);return}let o=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===s&&(o=!0,m.registerTexture(this.url,i,r.lods?.length,s,r));o||this.parser.getDependency("texture",s).then(i=>{i&&m.registerTexture(this.url,i,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[F];if(r&&r.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const i=this.parser.associations.get(o);i?.meshes===s&&m.registerMesh(this.url,r.guid,o,r.lods.length,i.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,o)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const u=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${u}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[le]=o);const i=o.guid;m.assignLODInformation(t,e,i,s,r),m.lodInfos.set(i,o),m.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,r,o,i)=>{const u=s.geometry;if(!u){g&&console.warn("gltf-progressive: Register mesh without geometry");return}u.userData||(u.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:o,uuid:s.uuid},i,s),m.assignLODInformation(t,u,e,r,o),m.lodInfos.set(e,i);let l=m.lowresCache.get(e)?.deref();l?l.push(s.geometry):l=[s.geometry],m.lowresCache.set(e,new WeakRef(l)),r>0&&!E(s)&&Ae(s,u);for(const c of q)c.onRegisteredNewMesh?.(s,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(g||H)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(
|
|
3
|
-
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const
|
|
4
|
-
`,
|
|
5
|
-
`,l),null)),
|
|
1
|
+
"use strict";var Ne=Object.create;var Te=Object.defineProperty;var Xe=Object.getOwnPropertyDescriptor;var je=Object.getOwnPropertyNames;var Ke=Object.getPrototypeOf,Ye=Object.prototype.hasOwnProperty;var He=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of je(t))!Ye.call(o,r)&&r!==e&&Te(o,r,{get:()=>t[r],enumerable:!(s=Xe(t,r))||s.enumerable});return o};var Qe=(o,t,e)=>(e=o!=null?Ne(Ke(o)):{},He(t||!o||!o.__esModule?Te(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("three"),we=require("three/examples/jsm/loaders/GLTFLoader.js"),Je=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Ze=require("three/examples/jsm/loaders/DRACOLoader.js"),et=require("three/examples/jsm/loaders/KTX2Loader.js");var ue=typeof document<"u"?document.currentScript:null;const Pe="";globalThis.GLTF_PROGRESSIVE_VERSION=Pe;console.debug("[gltf-progressive] version -");let $="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",X="https://cdn.needle.tools/static/three/0.179.1/basis2/";const tt=$,st=X,Re=new URL($+"draco_decoder.js");Re.searchParams.append("range","true");fetch(Re,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${$} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),$===tt&&ke("./include/draco/"),X===st&&Ae("./include/ktx2/")}).finally(()=>{Ie()});const rt=()=>({dracoDecoderPath:$,ktx2TranscoderPath:X});function ke(o){$=o,R&&R[pe]!=$?(console.debug("Updating Draco decoder path to "+o),R[pe]=$,R.setDecoderPath($),R.preload()):console.debug("Setting Draco decoder path to "+o)}function Ae(o){X=o,B&&B.transcoderPath!=X?(console.debug("Updating KTX2 transcoder path to "+o),B.setTranscoderPath(X),B.init()):console.debug("Setting KTX2 transcoder path to "+o)}function ie(o){return Ie(),o?B.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:R,ktx2Loader:B,meshoptDecoder:oe}}function Le(o){o.dracoLoader||o.setDRACOLoader(R),o.ktx2Loader||o.setKTX2Loader(B),o.meshoptDecoder||o.setMeshoptDecoder(oe)}const pe=Symbol("dracoDecoderPath");let R,oe,B;function Ie(){R||(R=new Ze.DRACOLoader,R[pe]=$,R.setDecoderPath($),R.setDecoderConfig({type:"js"}),R.preload()),B||(B=new et.KTX2Loader,B.setTranscoderPath(X),B.init()),oe||(oe=Je.MeshoptDecoder)}const me=new WeakMap;function ve(o,t){let e=me.get(o);e?e=Object.assign(e,t):e=t,me.set(o,e)}const nt=we.GLTFLoader.prototype.load;function ot(...o){const t=me.get(this);let e=o[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const n=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";n?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=s.toString()}return o[0]=e,nt?.call(this,...o)}we.GLTFLoader.prototype.load=ot;z("debugprogressive");function z(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 it(o,t){if(t===void 0||o===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))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}function Me(){return Q!==void 0||(Q=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",Q)),Q}let Q;function $e(){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 at{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof window<"u"&&window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};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 finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}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:r}=this._queue.shift();r({use:n=>this.add(s,n)})}}}function ut(o){const t=o.image?.width??0,e=o.image?.height??0,s=o.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,n=lt(o);return t*e*s*n*(1-Math.pow(.25,r))/(1-.25)}function lt(o){let t=4;const e=o.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=o.type;return r===1009||r===1010?s=1:r===1011||r===1012?s=2:r===1013||r===1014||r===1015?s=4:r===1016&&(s=2),t*s}const ct=typeof window>"u"&&typeof document>"u",ye=Symbol("needle:raycast-mesh");function H(o){return o?.[ye]instanceof f.BufferGeometry?o[ye]:null}function Be(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!H(o)){const s=dt(t);s.userData={isRaycastMesh:!0},o[ye]=s}}function Ge(o=!0){if(o){if(J)return;const t=J=f.Mesh.prototype.raycast;f.Mesh.prototype.raycast=function(e,s){const r=this,n=H(r);let i;n&&r.isMesh&&(i=r.geometry,r.geometry=n),t.call(this,e,s),i&&(r.geometry=i)}}else{if(!J)return;f.Mesh.prototype.raycast=J,J=null}}let J=null;function dt(o){const t=new f.BufferGeometry;for(const e in o.attributes)t.setAttribute(e,o.getAttribute(e));return t.setIndex(o.getIndex()),t}const V=new Array,g=z("debugprogressive");let te,N=-1;if(g){let o=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(N=s,console.log(`Set LOD level to [${N}]`))})}function qe(o){if(g&&te!==void 0)if(Array.isArray(o))for(const t of o)qe(t);else o&&"wireframe"in o&&(o.wireframe=te===!0)}const Z=new Array;let ft=0;const ht=Me()?2:10;function gt(o){if(Z.length<ht){const s=Z.length;g&&console.warn(`[Worker] Creating new worker #${s}`);const r=_e.createWorker(o||{});return Z.push(r),r}const t=ft++%Z.length;return Z[t]}class _e{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const n of this._running)if(n.url===r.result.url){pt(r.result),n.resolve(r.result);const i=n.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ue&&ue.tagName.toUpperCase()==="SCRIPT"&&ue.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new _e(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=rt();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),r=await this._webglRenderer);const a=ie(r).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const u={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:a};return this._debug&&console.debug("[Worker] Sending load request",u),this.worker.postMessage(u),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function pt(o){for(const t of o.geometries){const e=t.geometry,s=new f.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(le(r))}for(const r in e.attributes){const n=e.attributes[r],i=le(n);s.setAttribute(r,i)}if(e.morphAttributes)for(const r in e.morphAttributes){const i=e.morphAttributes[r].map(a=>le(a));s.morphAttributes[r]=i}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new f.Box3,s.boundingBox.min=new f.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new f.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new f.Sphere(new f.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of o.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,n=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;s=new f.CompressedTexture(r,n,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new f.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new f.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return o}function le(o){let t=o;if("isInterleavedBufferAttribute"in o&&o.isInterleavedBufferAttribute){const e=o.data,s=e.array,r=new f.InterleavedBuffer(s,e.stride);t=new f.InterleavedBufferAttribute(r,o.itemSize,s.byteOffset,o.normalized),t.offset=o.offset}else"isBufferAttribute"in o&&o.isBufferAttribute&&(t=new f.BufferAttribute(o.array,o.itemSize,o.normalized),t.usage=o.usage,t.gpuType=o.gpuType,t.updateRanges=o.updateRanges);return t}const mt=z("gltf-progressive-worker");z("gltf-progressive-reduce-mipmaps");const ee=z("gltf-progressive-gc"),ce=Symbol("needle-progressive-texture"),q="NEEDLE_progressive";class p{get name(){return q}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",n=t[r];if(n!=null)return n;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[r]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const u of Object.keys(a.uniforms)){const l=a.uniforms[u].value;l?.isTexture===!0&&i(l,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const u=t[a];u?.isTexture===!0&&i(u,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function i(a,u){const l=s.getAssignedLODInformation(a);if(l){const d=s.lodInfos.get(l.key);if(d&&d.lods){u.min_count=Math.min(u.min_count,d.lods.length),u.max_count=Math.max(u.max_count,d.lods.length);for(let c=0;c<d.lods.length;c++){const h=d.lods[c];h.width&&(u.lods[c]=u.lods[c]||{min_height:1/0,max_height:0},u.lods[c].min_height=Math.min(u.lods[c].min_height,h.height),u.lods[c].max_height=Math.max(u.lods[c].max_height,h.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const n of t)if(this.hasLODLevelAvailable(n,e))return!0;return!1}if(t.isMaterial===!0){for(const n of Object.keys(t)){const i=t[n];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const n of t.children)if(n.isMesh===!0&&this.hasLODLevelAvailable(n,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const n=s.userData.LODS;if(r=this.lodInfos.get(n.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e,s){if(!t)return Promise.resolve(null);if(t instanceof f.Mesh||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const a of V)a.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return p.getOrLoadLOD(r,e,{isCurrent:i}).then(a=>{if(Array.isArray(a)){const l=n.index||0;a=a[l]}const u=t["LOD:requested level"]===e;return(u||this.shouldApplyStaleMeshLOD(t,e))&&(u&&delete t["LOD:requested level"],a&&r!=a&&(a?.isBufferGeometry?typeof s?.apply=="function"?s.apply(a,e,t):s?.apply!==!1&&(t.geometry=a):g&&console.error("Invalid LOD geometry",a))),a}).catch(a=>(console.error("Error loading mesh LOD",t,a),null))}else g&&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,s){if(!t)return Promise.resolve(null);const r=s?.force===!0;if(t.isMesh===!0){const n=t;if(Array.isArray(n.material)){const i=new Array;for(const a of n.material){const u=this.assignTextureLOD(a,e,s);i.push(u)}return Promise.all(i).then(a=>{const u=new Array;for(const l of a)Array.isArray(l)&&u.push(...l);return u})}else return this.assignTextureLOD(n.material,e,s)}if(t.isMaterial===!0){const n=t,i=[],a=new Array;if(this.trackCurrentMaterialTextureSlots(n),n.uniforms&&(n.isRawShaderMaterial||n.isShaderMaterial===!0)){const u=n;for(const l of Object.keys(u.uniforms)){const d=u.uniforms[l].value;if(d?.isTexture===!0){const c=this.assignTextureLODForSlot(d,e,n,l,r).then(h=>(h&&u.uniforms[l].value!=h&&(u.uniforms[l].value=h,u.uniformsNeedUpdate=!0),h));i.push(c),a.push(l)}}}else for(const u of Object.keys(n)){const l=n[u];if(l?.isTexture===!0){const d=this.assignTextureLODForSlot(l,e,n,u,r);i.push(d),a.push(u)}}return Promise.all(i).then(u=>{const l=new Array;for(let d=0;d<u.length;d++){const c=u[d],h=a[d];c&&c.isTexture===!0?l.push({material:n,slot:h,texture:c,level:e}):l.push({material:n,slot:h,texture:null,level:e})}return l})}if(t instanceof f.Texture||t.isTexture===!0){const n=t;return this.assignTextureLODForSlot(n,e,null,null,r)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){p.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return p.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r,n){if(t?.isTexture!==!0)return Promise.resolve(null);if(r==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!n&&i.level<e))return Promise.resolve(t);if(s&&r){const c=this.getPendingTextureSlotRequest(s,r);if(c&&c.level===e&&c.force===n)return c.promise}const a=s&&r?this.nextTextureSlotRequestId(s,r,e,n):0,u=()=>!s||!r||this.getLatestTextureSlotRequest(s,r)?.id===a,l=()=>u()||this.shouldApplyStaleTextureSlotLOD(s,r,e,n),d=p.getOrLoadLOD(t,e,{isCurrent:l}).then(c=>{if(!u()&&!this.shouldApplyStaleTextureSlotLOD(s,r,e,n))return null;if(Array.isArray(c))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(c?.isTexture===!0){if(c!=t&&s&&r){const h=this.getMaterialTextureSlot(s,r)??t;if(h&&!n){const m=this.getAssignedLODInformation(h);if(m&&m?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",m.level,e,s,h,c),null}this.assignTrackedTextureSlot(s,r,c)}return c}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(c=>(console.error("Error loading LOD",t,c),null));return s&&r&&this.setPendingTextureSlotRequest(s,r,e,n,a,d),d}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const s of Object.keys(e.uniforms)){const r=e.uniforms[s].value;r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,s,r)}return}for(const e of Object.keys(t)){const s=t[e];s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,s)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,s,r){let n=this.latestTextureSlotRequests.get(t);n||(n=new Map,this.latestTextureSlotRequests.set(t,n));const i=++this.textureSlotRequestId;return n.set(e,{id:i,level:s,force:r}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,s,r){if(!t||!e)return!1;const n=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),a=this.getAssignedLODInformation(i)?.level??1/0;return s>=a?!1:r?n?s>=n.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const s=t["LOD:requested level"];if(typeof s!="number")return!1;const r=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<r&&e>=s}static setPendingTextureSlotRequest(t,e,s,r,n,i){let a=this.pendingTextureSlotRequests.get(t);a||(a=new Map,this.pendingTextureSlotRequests.set(t,a));const u={level:s,force:r,id:n,promise:i};a.set(e,u),i.finally(()=>{a.get(e)?.id===n&&a.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const n=t[e];return n?.isTexture===!0?n:null}static setMaterialTextureSlot(t,e,s){const n=t.uniforms?.[e];if(n?.value?.isTexture===!0){n.value=s,t.uniformsNeedUpdate=!0;return}t[e]=s}static assignTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const n=this.getMaterialTextureSlot(t,e);let i=r.get(e);!i&&n?i=this.ensureTrackedTextureSlot(t,e,n):i&&n&&i!==n&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,n)),!(i===s&&n===s)&&(i&&i!==s&&this.releaseTrackedTextureSlot(t,e,i),i!==s&&(this.trackTextureUsage(s),r.set(e,s)),n!==s&&this.setMaterialTextureSlot(t,e,s))}static ensureTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const n=r.get(e);return n===s?n:(n&&this.releaseTrackedTextureSlot(t,e,n),this.trackTextureUsage(s),r.set(e,s),s)}static releaseTrackedTextureSlot(t,e,s){const r=this.trackedTextureSlots.get(t);if(r?.get(e)===s&&r.delete(e),this.untrackTextureUsage(s)&&(g||ee)){const i=this.getAssignedLODInformation(s);console.log(`[gltf-progressive] Disposed old texture LOD ${i?.level??"?"} for ${t.name||t.type}.${e}`,s.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[q];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&p.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[q];if(r){if(!r.lods){g&&console.warn("Texture has no LODs",r);return}let n=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===s&&(n=!0,p.registerTexture(this.url,i,r.lods?.length,s,r));n||this.parser.getDependency("texture",s).then(i=>{i&&p.registerTexture(this.url,i,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[q];if(r&&r.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const i=this.parser.associations.get(n);i?.meshes===s&&p.registerMesh(this.url,r.guid,n,r.lods.length,i.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,n)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const a=e.image?.width||e.source?.data?.width||0,u=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${a}x${u}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`,n,e)}e.source&&(e.source[ce]=n);const i=n.guid;p.assignLODInformation(t,e,i,s,r),p.lodInfos.set(i,n),p.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,r,n,i)=>{const a=s.geometry;if(!a){g&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:n,uuid:s.uuid},i,s),p.assignLODInformation(t,a,e,r,n),p.lodInfos.set(e,i);let l=p.lowresCache.get(e)?.deref();l?l.push(s.geometry):l=[s.geometry],p.lowresCache.set(e,new WeakRef(l)),r>0&&!H(s)&&Be(s,a);for(const d of V)d.onRegisteredNewMesh?.(s,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=p.cache.get(t);(g||ee)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(p.cache.delete(t),(g||ee)&&console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),g==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(g==="verbose"||ee)&&n("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const r=s-1;if(r<=0)return this.textureRefCounts.delete(e),(g||ee)&&n("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),g==="verbose"&&n("[gltf-progressive] Memory: Untrack texture",r),!1;function n(i,a){const u=t.image?.width||t.source?.data?.width||0,l=t.image?.height||t.source?.data?.height||0,d=u&&l?`${u}x${l}`:"N/A";let c="N/A";u&&l&&(c=`~${(ut(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} — ${t.name} ${d} (${c}), refCount: ${s} → ${a}
|
|
3
|
+
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,s){const r=g=="verbose",n=this.getAssignedLODInformation(t);if(!n)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const i=n?.key;let a;if(t.isTexture===!0){const l=t;l.source&&l.source[ce]&&(a=l.source[ce])}if(a||(a=p.lodInfos.get(i)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`,t.type,p.lodInfos);else{if(e>0){let c=!1;const h=Array.isArray(a.lods);if(h&&e>=a.lods.length?c=!0:h||(c=!0),c){const m=this.lowresCache.get(i);if(m){const O=m.deref();if(O)return O;this.lowresCache.delete(i),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${i}`)}return null}}const l=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!l)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const d=it(n.url,l);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const c=d+"_"+a.guid,h=await this.tryResolveLODCacheEntry(this.cache.get(c),c,d,t,e,r);if(h.found)return h.value;if(s?.isCurrent?.()===!1)return r&&console.log(`Skipping stale LOD ${e} request before queue: ${d}`),null;const m=await this.queue.slot(d);if(s?.isCurrent?.()===!1)return r&&console.log(`Skipping stale LOD ${e} request after queue: ${d}`),null;const O=await this.tryResolveLODCacheEntry(this.cache.get(c),c,d,t,e,r);if(O.found)return O.value;if(!m.use)return g&&console.log(`LOD ${e} was aborted: ${d}`),null;const x=a,w=new Promise(async(v,E)=>{if(mt){const y=await(await gt({})).load(d);if(y.textures.length>0)for(const D of y.textures){let T=D.texture;return p.assignLODInformation(n.url,T,i,e,void 0),t instanceof f.Texture&&(T=this.copySettings(t,T)),T&&(T.guid=x.guid),v(T)}if(y.geometries.length>0){const D=new Array;for(const T of y.geometries){const G=T.geometry;p.assignLODInformation(n.url,G,i,e,T.primitiveIndex),D.push(G)}return v(D)}return v(null)}const L=new we.GLTFLoader;Le(L),g&&(await new Promise(b=>setTimeout(b,1e3)),r&&console.warn("Start loading (delayed) "+d,x.guid));let S=d;if(x&&Array.isArray(x.lods)){const b=x.lods[e];b.hash&&(S+="?v="+b.hash)}const _=await L.loadAsync(S).catch(b=>(console.error(`Error loading LOD ${e} from ${d}
|
|
4
|
+
`,b),v(null)));if(!_)return v(null);const A=_.parser;r&&console.log("Loading finished "+d,x.guid);let P=0;if(_.parser.json.textures){let b=!1;for(const y of _.parser.json.textures){if(y?.extensions){const D=y?.extensions[q];if(D?.guid&&D.guid===x.guid){b=!0;break}}P++}if(b){let y=await A.getDependency("texture",P);return y&&p.assignLODInformation(n.url,y,i,e,void 0),r&&console.log('change "'+t.name+'" → "'+y.name+'"',d,P,y,c),t instanceof f.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=x.guid),v(y)}else g&&console.warn("Could not find texture with guid",x.guid,_.parser.json)}if(P=0,_.parser.json.meshes){let b=!1;for(const y of _.parser.json.meshes){if(y?.extensions){const D=y?.extensions[q];if(D?.guid&&D.guid===x.guid){b=!0;break}}P++}if(b){const y=await A.getDependency("mesh",P);if(r&&console.log(`Loaded Mesh "${y.name}"`,d,P,y,c),y.isMesh===!0){const D=y.geometry;return p.assignLODInformation(n.url,D,i,e,0),v(D)}else{const D=new Array;for(let T=0;T<y.children.length;T++){const G=y.children[T];if(G.isMesh===!0){const se=G.geometry;p.assignLODInformation(n.url,se,i,e,T),D.push(se)}}return v(D)}}else g&&console.warn("Could not find mesh with guid",x.guid,_.parser.json)}return v(null)});this.cache.set(c,w),m.use(w);const M=await w;return M!=null?M instanceof f.Texture?(this.cache.set(c,new WeakRef(M)),p._resourceRegistry.register(M,c)):Array.isArray(M)?this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(null)),M}else if(t instanceof f.Texture){if(s?.isCurrent?.()===!1)return r&&console.log(`Skipping stale texture LOD ${e} request: ${d}`),null;r&&console.log("Load texture from uri: "+d);const h=await new f.TextureLoader().loadAsync(d);return s?.isCurrent?.()===!1?(h?.dispose(),null):(h?(h.guid=a.guid,h.flipY=!1,h.needsUpdate=!0,h.colorSpace=t.colorSpace,r&&console.log(a,h)):g&&console.warn("failed loading",d),h)}}return null}static async tryResolveLODCacheEntry(t,e,s,r,n,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${n} was already loading/loaded: ${e}`),t instanceof WeakRef){const l=t.deref();if(l){let d=l,c=!1;if(d instanceof f.Texture&&r instanceof f.Texture?d.image?.data||d.source?.data?d=this.copySettings(r,d):c=!0:d instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(d.attributes.position?.array||(c=!0)),!c)return{found:!0,value:d}}return this.cache.delete(e),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let a=await t.catch(l=>(console.error(`Error loading LOD ${n} from ${s}
|
|
5
|
+
`,l),null)),u=!1;return a==null||(a instanceof f.Texture&&r instanceof f.Texture?a.image?.data||a.source?.data?a=this.copySettings(r,a):(u=!0,this.cache.delete(e)):a instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(a.attributes.position?.array||(u=!0,this.cache.delete(e)))),u?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new at(Me()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const i=new yt(t,s,r,n);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
|
|
6
6
|
`,t.uuid,`
|
|
7
|
-
`,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}}class gt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ue{static addPromise=(t,e,s,r)=>{r.forEach(o=>{o.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const r=this._seen.get(e);if(r>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+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(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const A=z("debugprogressive"),pt=A==="colors",mt=z("noprogressive"),ce=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),b={mesh_lod:-1,texture_lod:-1},yt=new d.Color,me=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];class _{static debugDrawLine;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[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const s=new _(t,{engine:"unknown",...e});return t[ce]=s,s}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return q}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ue(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(s);o>=0&&(this._newPromiseGroups.splice(o,1),Te()&&performance.measure("LODsManager:awaitLoading",{start:r,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(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let s=!1;if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&(this._lodchangedlisteners.splice(r,1),s=!0)}return s}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new d.Clock;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;re(this.renderer),this.renderer.render=function(s,r){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((u,a)=>u+a)/e._fpsBuffer.length,A&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=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 i=!0;if(o.length===1){const u=o[0].material;(u.name==="EffectMaterial"||u.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(mt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,A&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,A&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of r){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){A&&(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}const l=a.object;(l instanceof d.Mesh||l.isMesh)&&this.updateLODs(t,e,l,o)}const i=s.transparent;for(const a of i){const l=a.object;(l instanceof d.Mesh||l.isMesh)&&this.updateLODs(t,e,l,o)}const u=s.transmissive;for(const a of u){const l=a.object;(l instanceof d.Mesh||l.isMesh)&&this.updateLODs(t,e,l,o)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let o=s[de];if(o||(o=new xt,s[de]=o),o.frames++<2)return;for(const u of q)u.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(b.mesh_lod=i,b.texture_lod=i):(this.calculateLodLevel(e,s,o,r,b),b.mesh_lod=Math.round(b.mesh_lod),b.texture_lod=Math.round(b.texture_lod)),b.mesh_lod>=0&&this.loadProgressiveMeshes(s,b.mesh_lod),s.material&&b.texture_lod>=0&&this.loadProgressiveTextures(s.material,b.texture_lod,i),g&&s.material&&!s.isGizmo&&Be(s.material),pt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&$e(s.material,b.mesh_lod);for(const u of q)u.onAfterUpdatedLOD?.(this.renderer,t,e,s,b);o.lastLodLevel_Mesh=b.mesh_lod,o.lastLodLevel_Texture=b.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,s);return}let r=!1;(t[W]===void 0||e<t[W])&&(r=!0);const o=s!==void 0&&s>=0;if(o&&(r=t[W]!=s,e=s),r){t[W]=e;const i=o?{force:!0}:void 0,u=m.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,u,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[W]!=r,e=r),s){t[W]=e;const o=t.geometry,i=m.assignMeshLOD(t,e).then(u=>(u&&t[W]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),u));return ue.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const s=t.min,r=t.max,o=(s.x+r.x)*.5,i=(s.y+r.y)*.5;return this._tempPtInside.set(o,i,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let u=10+1,a=!1;if(A&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=m.getMeshLODExtension(e.geometry)?.lods,c=m.getPrimitiveIndex(e.geometry),h=l&&l.length>0,x=m.getMaterialMinMaxLODsCount(e.material),S=x.min_count!==1/0&&x.min_count>=0&&x.max_count>=0;if(!h&&!S){o.mesh_lod=0,o.texture_lod=0;return}h||(a=!0,u=0);const v=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let k=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const w=e;if(!w.boundingBox)w.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!w[_.$skinnedMeshBoundsOffset]){const $=_.skinnedMeshBoundsFrameOffsetCounter++;w[_.$skinnedMeshBoundsOffset]=$}const y=w[_.$skinnedMeshBoundsOffset];if((s.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const $=E(w),G=w.geometry;$&&(w.geometry=$),w.computeBoundingBox(),w.geometry=G}}k=w.boundingBox}if(k){const w=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const f=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f)){o.mesh_lod=0,o.texture_lod=0;return}}if(this._tempBox.copy(k),this._tempBox.applyMatrix4(e.matrixWorld),w.isPerspectiveCamera&&_.isInside(this._tempBox,this.projectionScreenMatrix)){o.mesh_lod=0,o.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&w.isPerspectiveCamera&&w.fov>70){const f=this._tempBox.min,p=this._tempBox.max;let L=f.x,M=f.y,C=p.x,X=p.y;const J=2,oe=1.5,Z=(f.x+p.x)*.5,ee=(f.y+p.y)*.5;L=(L-Z)*J+Z,M=(M-ee)*J+ee,C=(C-Z)*J+Z,X=(X-ee)*J+ee;const We=L<0&&C>0?0:Math.min(Math.abs(f.x),Math.abs(p.x)),qe=M<0&&X>0?0:Math.min(Math.abs(f.y),Math.abs(p.y)),ne=Math.max(We,qe);s.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else s.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&v>0&&y.multiplyScalar(v/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const $=t.matrixWorldInverse,G=this._tempBox2;G.copy(k),G.applyMatrix4(e.matrixWorld),G.applyMatrix4($);const R=G.getSize(this._tempBox2Size),O=Math.max(R.x,R.y);if(Math.max(y.x,y.y)!=0&&O!=0&&(y.z=R.z/Math.max(R.x,R.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,A&&_.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const p=_.corner0,L=_.corner1,M=_.corner2,C=_.corner3;p.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=p.x,M.copy(this._tempBox.max),M.y=p.y,C.copy(this._tempBox.max);const X=(p.z+C.z)*.5;p.z=L.z=M.z=C.z=X,p.applyMatrix4(f),L.applyMatrix4(f),M.applyMatrix4(f),C.applyMatrix4(f),_.debugDrawLine(p,L,255),_.debugDrawLine(p,M,255),_.debugDrawLine(L,C,255),_.debugDrawLine(M,C,255)}let T=999;if(l&&s.lastScreenCoverage>0)for(let f=0;f<l.length;f++){const p=l[f],M=(p.densities?.[c]||p.density||1e-5)/s.lastScreenCoverage;if(c>0&&Te()&&!p.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),M<r){T=f;break}}T<u&&(u=T,a=!0)}if(a?o.mesh_lod=u:o.mesh_lod=s.lastLodLevel_Mesh,A&&o.mesh_lod!=s.lastLodLevel_Mesh){const y=l?.[o.mesh_lod];y&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${o.mesh_lod} (density: ${y.densities?.[c].toFixed(0)}) | ${e.name}`)}if(S){const w="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(o.texture_lod=x.max_count-1,A){const y=x.lods[x.max_count-1];A&&console.log(`First Texture LOD ${o.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let $=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&($*=1.5);const R=v/window.devicePixelRatio*$;let O=!1;for(let U=x.lods.length-1;U>=0;U--){const T=x.lods[U];if(!(w&&T.max_height>=2048)&&!(Le()&&T.max_height>4096)&&(T.max_height>R||!O&&U===0)){if(O=!0,o.texture_lod=U,A&&o.texture_lod<s.lastLodLevel_Texture){const D=T.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${o.texture_lod} = ${D}px
|
|
8
|
-
Screensize: ${
|
|
9
|
-
${e.name}`)}break}}}}else
|
|
10
|
-
`,
|
|
7
|
+
`,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}}class yt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class re{static addPromise=(t,e,s,r)=>{r.forEach(n=>{n.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(n=>{this._resolve=n}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const r=this._seen.get(e);if(r>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+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(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const I=z("debugprogressive"),xt=I==="colors",wt=z("noprogressive"),de=Symbol("Needle:LODSManager"),fe=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),C={mesh_lod:-1,texture_lod:-1},Lt=new f.Color,xe=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405],he=new f.Box3,F=new f.Box3,Oe=new f.Box3,vt=new f.Vector3,Mt=new f.Vector3,_t=new f.Matrix4,W=new f.Vector3,j=new f.Vector3,K=new f.Vector3,Y=new f.Vector3;function Dt(o,t){const e=o.min,s=o.max,r=(e.x+s.x)*.5,n=(e.y+s.y)*.5;return W.set(r,n,e.z).applyMatrix4(t).z<0}function We(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:u=!1,debugDrawLine:l,warnMissingPrimitiveDensities:d=!1}=o,c=p.getMeshLODExtension(t)?.lods,h=p.getPrimitiveIndex(t),m=o.target??{level:a,primitiveIndex:h,screenCoverage:0,screenspaceVolume:new f.Vector3,centrality:1};if(m.level=a,m.primitiveIndex=h,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1,!c?.length)return m;let O=o.boundingBox??t.boundingBox;if(O||(t.computeBoundingBox(),O=t.boundingBox),!O)return m;if(he.copy(O).applyMatrix4(e),s.isPerspectiveCamera&&Dt(he,r))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(F.copy(he).applyMatrix4(r),u&&s.isPerspectiveCamera&&s.fov>70){const L=F.min,S=F.max;let _=L.x,A=L.y,P=S.x,b=S.y;const y=2,D=1.5,T=(L.x+S.x)*.5,G=(L.y+S.y)*.5;_=(_-T)*y+T,A=(A-G)*y+G,P=(P-T)*y+T,b=(b-G)*y+G;const se=_<0&&P>0?0:Math.min(Math.abs(L.x),Math.abs(S.x)),Ee=A<0&&b>0?0:Math.min(Math.abs(L.y),Math.abs(S.y)),ae=Math.max(se,Ee);m.centrality=(D-ae)*(D-ae)*(D-ae)}const x=F.getSize(vt);x.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&x.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(x.x*=s.aspect),Oe.copy(O).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const w=Oe.getSize(Mt),M=Math.max(x.x,x.y),v=Math.max(w.x,w.y);M!==0&&v!==0&&(x.z=w.z/v*M);const E=Math.max(x.x,x.y,x.z)*m.centrality;if(m.screenCoverage=E,m.screenspaceVolume.copy(x),E<=0)return m;if(l){const L=_t.copy(r);L.invert(),W.copy(F.min),j.copy(F.max),j.x=W.x,K.copy(F.max),K.y=W.y,Y.copy(F.max);const S=(W.z+Y.z)*.5;W.z=j.z=K.z=Y.z=S,W.applyMatrix4(L),j.applyMatrix4(L),K.applyMatrix4(L),Y.applyMatrix4(L),l(W,j,255),l(W,K,255),l(j,Y,255),l(K,Y,255)}for(let L=0;L<c.length;L++){const S=c[L],_=S.densities?.[h]||S.density||1e-5;if(h>0&&d&&$e()&&!S.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),_/E<n){m.level=L;break}}return m}class k{static debugDrawLine;static getObjectLODState(t){return t[fe]}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[de])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[de];const s=new k(t,{engine:"unknown",...e});return t[de]=s,s}renderer;context;projectionScreenMatrix=new f.Matrix4;get plugins(){return V}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new re(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const n=this._newPromiseGroups.indexOf(s);n>=0&&(this._newPromiseGroups.splice(n,1),$e()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}trackLoadingPromise(t,e,s){return re.addPromise(t,e,s,this._newPromiseGroups),s}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let s=!1;if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&(this._lodchangedlisteners.splice(r,1),s=!0)}return s}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new f.Clock;#r=0;#n=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ie(this.renderer),this.renderer.render=function(s,r){const n=e.renderer.getRenderTarget();(n==null||"isXRRenderTarget"in n&&n.isXRRenderTarget)&&(t=0,e.#r+=1,e.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((a,u)=>a+u)/e._fpsBuffer.length,I&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=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 i=!0;if(n.length===1){const a=n[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(wt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,I&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,I&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const u of r){if(u.material&&(u.geometry?.type==="BoxGeometry"||u.geometry?.type==="BufferGeometry")&&(u.material.name==="SphericalGaussianBlur"||u.material.name=="BackgroundCubeMaterial"||u.material.name==="CubemapFromEquirect"||u.material.name==="EquirectangularToCubeUV")){I&&(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(u.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",u,u.material.name,u.material.type)));continue}switch(u.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const l=u.object;(l instanceof f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const i=s.transparent;for(const u of i){const l=u.object;(l instanceof f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const a=s.transmissive;for(const u of a){const l=u.object;(l instanceof f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[fe];if(n||(n=new Tt,s[fe]=n),n.frames++<2)return;for(const a of V)a.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(C.mesh_lod=i,C.texture_lod=i):(this.calculateLodLevel(e,s,n,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,i),g&&s.material&&!s.isGizmo&&qe(s.material),xt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&Ue(s.material,C.mesh_lod);for(const a of V)a.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);n.lastLodLevel_Mesh=C.mesh_lod,n.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,s);return}let r=!1;(t[U]===void 0||e<t[U])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[U]!=s,e=s),r){t[U]=e;const i=n?{force:!0}:void 0,a=p.assignTextureLOD(t,e,i).then(u=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});re.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[U]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[U]!=r,e=r),s){t[U]=e;const n=t.geometry,i=p.assignMeshLOD(t,e).then(a=>(a&&t[U]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(u=>u({type:"mesh",level:e,object:t})),a));return re.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new f.Sphere;_tempWorldPosition=new f.Vector3;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,n){if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1,u=!1;if(I&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=p.getMeshLODExtension(e.geometry)?.lods,d=p.getPrimitiveIndex(e.geometry),c=l&&l.length>0,h=p.getMaterialMinMaxLODsCount(e.material),m=h.min_count!==1/0&&h.min_count>=0&&h.max_count>=0;if(!c&&!m){n.mesh_lod=0,n.texture_lod=0;return}c||(u=!0,a=0);const O=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let x=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const w=e;if(!w.boundingBox)w.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!w[k.$skinnedMeshBoundsOffset]){const v=k.skinnedMeshBoundsFrameOffsetCounter++;w[k.$skinnedMeshBoundsOffset]=v}const M=w[k.$skinnedMeshBoundsOffset];if((s.frames+M)%this.skinnedMeshAutoUpdateBoundsInterval===0){const v=H(w),E=w.geometry;v&&(w.geometry=v),w.computeBoundingBox(),w.geometry=E}}x=w.boundingBox}if(x){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 v=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(v)){n.mesh_lod=0,n.texture_lod=0;return}}const w=We({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:O,currentLevel:s.lastLodLevel_Mesh,boundingBox:x,xrEnabled:this.renderer.xr.enabled,debugDrawLine:I?k.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=w.centrality,s.lastScreenCoverage=w.screenCoverage,s.lastScreenspaceVolume.copy(w.screenspaceVolume),w.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}w.level>=0&&w.level<a&&(a=w.level,u=!0)}if(u?n.mesh_lod=a:n.mesh_lod=s.lastLodLevel_Mesh,I&&n.mesh_lod!=s.lastLodLevel_Mesh){const M=l?.[n.mesh_lod];M&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${M.densities?.[d].toFixed(0)}) | ${e.name}`)}if(m){const w="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=h.max_count-1,I){const M=h.lods[h.max_count-1];I&&console.log(`First Texture LOD ${n.texture_lod} (${M.max_height}px) - ${e.name}`)}}else{const M=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let v=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(v*=1.5);const L=O/window.devicePixelRatio*v;let S=!1;for(let _=h.lods.length-1;_>=0;_--){const A=h.lods[_];if(!(w&&A.max_height>=2048)&&!(Me()&&A.max_height>4096)&&(A.max_height>L||!S&&_===0)){if(S=!0,n.texture_lod=_,I&&n.texture_lod<s.lastLodLevel_Texture){const P=A.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${P}px
|
|
8
|
+
Screensize: ${L.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${M.toFixed(1)}
|
|
9
|
+
${e.name}`)}break}}}}else n.texture_lod=0}}class Tt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new f.Vector3;lastCentrality=0}function Ue(o,t){if(!(t<0)){if(Array.isArray(o)){for(const e of o)Ue(e,t);return}"color"in o&&o.color instanceof f.Color&&(o.color.copy(Fe(t,Lt)),o.needsUpdate=!0)}}function Fe(o,t){const e=Math.max(0,Math.min(xe.length-1,Math.floor(o)));return t.setHex(xe[e])}const Se=Symbol("NEEDLE_mesh_lod"),ne=Symbol("NEEDLE_texture_lod");let ge=null;function De(){const o=Ot();o&&(o.mapURLs(function(t){return be(),t}),be(),ge?.disconnect(),ge=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&Ve(s)})})}),ge.observe(document,{childList:!0,subtree:!0}))}function Ot(){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"),De()}),null)}function be(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Ve(t)})}const Ce=new WeakSet;let St=0;function Ve(o){if(!o||Ce.has(o))return null;Ce.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++St+`
|
|
10
|
+
`,o.getAttribute("src"));let t=null,e=null,s=null;for(let r=o;r!=null;r=Object.getPrototypeOf(r)){const n=Object.getOwnPropertySymbols(r),i=n.find(l=>l.toString()=="Symbol(renderer)"),a=n.find(l=>l.toString()=="Symbol(scene)"),u=n.find(l=>l.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&a!=null&&(e=o[a]),!s&&u!=null&&(s=o[u])}if(t&&e){let r=function(){if(s){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}s?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=k.get(t,{engine:"model-viewer"});return k.addPlugin(new bt),n.enable(),n.addEventListener("changed",()=>{s?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&s?.call(o)}),o.addEventListener("load",()=>{r()}),()=>{n.disable()}}return null}class bt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}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[ne]==!0)return;e[ne]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(n&&s&&e.material){let i=function(u){if(u[ne]==!0)return;u[ne]=!0,u.userData&&(u.userData.LOD=-1);const l=Object.keys(u);for(let d=0;d<l.length;d++){const c=l[d],h=u[c];if(h?.isTexture===!0){const m=h.userData?.associations?.textures;if(m==null)continue;const O=s.parser.json.textures[m];if(!O){console.warn("Texture data not found for texture index "+m);continue}if(O?.extensions?.[q]){const x=O.extensions[q];x&&n&&p.registerTexture(n,h,x.lods.length,m,x)}}}};const a=e.material;if(Array.isArray(a))for(const u of a)i(u);else i(a)}}tryParseMeshLOD(t,e){if(e[Se]==!0)return;e[Se]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const n=e.userData?.gltfExtensions?.[q];if(n&&r){const i=e.uuid;p.registerMesh(r,i,e,0,n.lods.length,n)}}}function ze(...o){let t,e,s,r;switch(o.length){case 2:[s,e]=o,r={};break;case 3:[s,e,r]=o;break;case 4:[t,e,s,r]=o;break;default:throw new Error("Invalid arguments")}ie(e),Le(s),ve(s,{progressive:!0,...r?.hints}),s.register(i=>new p(i));const n=k.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}De();if(!ct){const o={gltfProgressive:{useNeedleProgressive:ze,LODsManager:k,configureLoader:ve,getRaycastMesh:H,useRaycastMeshes:Ge}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=q;exports.LODsManager=k;exports.NEEDLE_progressive=p;exports.VERSION=Pe;exports.addDracoAndKTX2Loaders=Le;exports.calculateMeshLODLevel=We;exports.configureLoader=ve;exports.createLoaders=ie;exports.getLODColor=Fe;exports.getRaycastMesh=H;exports.lodDebugColors=xe;exports.patchModelViewer=De;exports.registerRaycastMesh=Be;exports.setDracoDecoderLocation=ke;exports.setKTX2TranscoderLocation=Ae;exports.useNeedleProgressive=ze;exports.useRaycastMeshes=Ge;
|
package/lib/extension.d.ts
CHANGED
|
@@ -92,7 +92,10 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
92
92
|
* });
|
|
93
93
|
* ```
|
|
94
94
|
*/
|
|
95
|
-
static assignMeshLOD(mesh: Mesh, level: number
|
|
95
|
+
static assignMeshLOD(mesh: Mesh, level: number, options?: {
|
|
96
|
+
/** If false, load and return the requested geometry without assigning it to the mesh. Pass a function to apply it manually. */
|
|
97
|
+
apply?: boolean | ((geometry: BufferGeometry, level: number, mesh: Mesh) => boolean | void);
|
|
98
|
+
}): Promise<BufferGeometry | null>;
|
|
96
99
|
/** Load a different resolution of a texture (if available)
|
|
97
100
|
* @param context the context
|
|
98
101
|
* @param source the sourceid of the file from which the texture is loaded (this is usually the component's sourceId)
|
|
@@ -112,8 +115,14 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
112
115
|
private static assignTextureLODForSlot;
|
|
113
116
|
private static trackedTextureSlots;
|
|
114
117
|
private static pendingTextureSlotRequests;
|
|
118
|
+
private static latestTextureSlotRequests;
|
|
119
|
+
private static textureSlotRequestId;
|
|
115
120
|
private static trackCurrentMaterialTextureSlots;
|
|
116
121
|
private static getPendingTextureSlotRequest;
|
|
122
|
+
private static nextTextureSlotRequestId;
|
|
123
|
+
private static getLatestTextureSlotRequest;
|
|
124
|
+
private static shouldApplyStaleTextureSlotLOD;
|
|
125
|
+
private static shouldApplyStaleMeshLOD;
|
|
117
126
|
private static setPendingTextureSlotRequest;
|
|
118
127
|
private static getMaterialTextureSlot;
|
|
119
128
|
private static setMaterialTextureSlot;
|