@needle-tools/gltf-progressive 4.0.0-alpha.4 → 4.0.0-alpha.5
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 +3 -0
- package/gltf-progressive.js +1 -1
- package/gltf-progressive.min.js +1 -1
- package/gltf-progressive.umd.cjs +7 -7
- package/lib/utils.internal.js +3 -8
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,9 @@ All notable changes to this package will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [4.0.0-alpha.5] - 2026-10-06
|
|
8
|
+
- Fix: Progressive LOD loading starts during an immersive WebXR session when the window animation frame callback is paused.
|
|
9
|
+
|
|
7
10
|
## [4.0.0-alpha.4] - 2026-10-02
|
|
8
11
|
- Fix: Server-side rendering no longer checks browser-only decoder URLs during import.
|
|
9
12
|
|
package/gltf-progressive.js
CHANGED
|
@@ -120,7 +120,7 @@ function Fe() {
|
|
|
120
120
|
}
|
|
121
121
|
class wt {
|
|
122
122
|
constructor(t, e = {}) {
|
|
123
|
-
this.maxConcurrent = t, this.debug = e.debug ?? !1,
|
|
123
|
+
this.maxConcurrent = t, this.debug = e.debug ?? !1, setTimeout(this.tick, 0);
|
|
124
124
|
}
|
|
125
125
|
_running = /* @__PURE__ */ new Map();
|
|
126
126
|
_queue = [];
|
package/gltf-progressive.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as Ze from"three";import{RedFormat as et,RedIntegerFormat as tt,RGFormat as rt,RGIntegerFormat as st,RGBFormat as ot,RGBAFormat as nt,RGBAIntegerFormat as it,BufferGeometry as z,Mesh as V,Box3 as ae,Vector3 as P,Sphere as Pe,CompressedTexture as at,Texture as G,Matrix3 as lt,InterleavedBuffer as ut,InterleavedBufferAttribute as ct,BufferAttribute as dt,TextureLoader as ht,Color as Ce,Matrix4 as Ae}from"three";import{GLTFLoader as ge}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as gt}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as ft}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as mt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ie="";globalThis.GLTF_PROGRESSIVE_VERSION=Ie,console.debug("[gltf-progressive] version -");let E="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",X="https://cdn.needle.tools/static/three/0.179.1/basis2/";const pt=E,xt=X;if(typeof window<"u"){const n=new URL(E+"draco_decoder.js");n.searchParams.append("range","true"),fetch(n,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${E} (offline: ${navigator.onLine})`),E===pt&&Ee("./include/draco/"),X===xt&&$e("./include/ktx2/")}).finally(()=>{Be()})}const yt=()=>({dracoDecoderPath:E,ktx2TranscoderPath:X});function Ee(n){E=n,C&&C[me]!=E?(console.debug("Updating Draco decoder path to "+n),C[me]=E,C.setDecoderPath(E),C.preload()):console.debug("Setting Draco decoder path to "+n)}function $e(n){X=n,$&&$.transcoderPath!=X?(console.debug("Updating KTX2 transcoder path to "+n),$.setTranscoderPath(X),$.init()):console.debug("Setting KTX2 transcoder path to "+n)}function le(n){return Be(),n?$.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:C,ktx2Loader:$,meshoptDecoder:ue}}function fe(n){n.dracoLoader||n.setDRACOLoader(C),n.ktx2Loader||n.setKTX2Loader($),n.meshoptDecoder||n.setMeshoptDecoder(ue)}const me=Symbol("dracoDecoderPath");let C,ue,$;function Be(){C||(C=new ft,C[me]=E,C.setDecoderPath(E),C.preload()),$||($=new mt,$.setTranscoderPath(X),$.init()),ue||(ue=gt)}const pe=new WeakMap;function xe(n,t){let e=pe.get(n);e?e=Object.assign(e,t):e=t,pe.set(n,e)}const vt=ge.prototype.load;function wt(...n){const t=pe.get(this);let e=n[0];const r=Lt(e);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,vt?.call(this,...n)}ge.prototype.load=wt;function Lt(n){try{if(n instanceof URL)return n;const t=globalThis.location?.href;return t?new URL(n,t):new URL(n)}catch{return null}}function Ge(n){return n!=null&&n.data!=null}function ye(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function bt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function ve(n){const t=bt(n),e=ye(n);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}j("debugprogressive");function j(n){const t=globalThis.location?.href;if(!t)return!1;const e=new URL(t).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Mt(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 we(){if(ee!==void 0)return ee;const n=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(n),j("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function qe(){const n=globalThis.location?.href;if(!n)return!1;const t=new URL(n),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class Dt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}_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 _t(n){const t=n.image,e=t?.width??0,r=t?.height??0,s=t?.depth??1,o=Math.floor(Math.log2(Math.max(e,r,s)))+1,i=Tt(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function Tt(n){let t=4;const e=n.format;e===et||e===tt?t=1:e===rt||e===st?t=2:e===ot||e===1029?t=3:(e===nt||e===it)&&(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 St=typeof window>"u"&&typeof document>"u",Le=Symbol("needle:raycast-mesh");function Y(n){return n?.[Le]instanceof z?n[Le]:null}function We(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=Ot(t);e.userData={isRaycastMesh:!0},n[Le]=e}}function Ne(n=!0){if(n){if(te)return;const t=te=V.prototype.raycast;V.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(!te)return;V.prototype.raycast=te,te=null}}let te=null;function Ot(n){const t=new z;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const W=new Array,g=j("debugprogressive");let re,H=-1;if(g&&typeof window<"u"){let n=function(){H+=1,H>=t&&(H=-1),console.log(`Toggle LOD level [${H}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(H=r,console.log(`Set LOD level to [${H}]`))})}function je(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)je(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let kt=0;const Rt=we()?2:10;function Pt(n){if(se.length<Rt){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Re.createWorker(n||{});return se.push(r),r}const t=kt++%se.length;return se[t]}class Re{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){Ct(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-Cj5rV8iI.js",import.meta.url),{type:"module"});return new Re(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=yt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),s=await this._webglRenderer);const o=le(s).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const l=globalThis.location?.href;l&&(t=new URL(t,l).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(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function Ct(n){for(const t of n.geometries){const e=t.geometry,r=new z;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(be(s))}for(const s in e.attributes){const o=e.attributes[s],i=be(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>be(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ae,r.boundingBox.min=new P(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new P(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Pe(new P(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,{width:o,height:i}=ve(e);r=new at(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new G(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 lt(...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 be(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new ut(r,e.stride);t=new ct(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new dt(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const At=j("gltf-progressive-worker");j("gltf-progressive-reduce-mipmaps");const oe=j("gltf-progressive-gc"),Me=Symbol("needle-progressive-texture"),q="NEEDLE_progressive";class f{get name(){return q}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 l of t)this.getMaterialMinMaxLODsCount(l,e);return t[s]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const c=l.uniforms[a].value;c?.isTexture===!0&&i(c,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(l,a){const c=r.getAssignedLODInformation(l);if(c){const d=r.lodInfos.get(c.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let u=0;u<d.lods.length;u++){const p=d.lods[u];p.width&&(a.lods[u]=a.lods[u]||{min_height:1/0,max_height:0},a.lods[u].min_height=Math.min(a.lods[u].min_height,p.height),a.lods[u].max_height=Math.max(a.lods[u].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 V||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const l of W)l.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(l=>{if(Array.isArray(l)){const c=o.index||0;l=l[c]}const a=t["LOD:requested level"]===e;return(a||this.shouldApplyStaleMeshLOD(t,e))&&(a&&delete t["LOD:requested level"],l&&s!=l&&(l?.isBufferGeometry?typeof r?.apply=="function"?r.apply(l,e,t):r?.apply!==!1&&(t.geometry=l):g&&console.error("Invalid LOD geometry",l))),l}).catch(l=>(console.error("Error loading mesh LOD",t,l),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 l of o.material){const a=this.assignTextureLOD(l,e,r);i.push(a)}return Promise.all(i).then(l=>{const a=new Array;for(const c of l)Array.isArray(c)&&a.push(...c);return a})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],l=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const a=o;for(const c of Object.keys(a.uniforms)){const d=a.uniforms[c].value;if(d?.isTexture===!0){const u=this.assignTextureLODForSlot(d,e,o,c,s).then(p=>(p&&a.uniforms[c].value!=p&&(a.uniforms[c].value=p,a.uniformsNeedUpdate=!0),p));i.push(u),l.push(c)}}}else for(const a of Object.keys(o)){const c=o[a];if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,a,s);i.push(d),l.push(a)}}return Promise.all(i).then(a=>{const c=new Array;for(let d=0;d<a.length;d++){const u=a[d],p=l[d];u&&u.isTexture===!0?c.push({material:o,slot:p,texture:u,level:e}):c.push({material:o,slot:p,texture:null,level:e})}return c})}if(t instanceof G||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 u=this.getPendingTextureSlotRequest(r,s);if(u&&u.level===e&&u.force===o)return u.promise}const l=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,a=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===l,c=()=>a()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),d=f.getOrLoadLOD(t,e,{isCurrent:c}).then(u=>{if(!a()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(u))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(u?.isTexture===!0){if(u!=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,u),null}this.assignTrackedTextureSlot(r,s,u)}return u}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(u=>(console.error("Error loading LOD",t,u),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,l,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 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),l=this.getAssignedLODInformation(i)?.level??1/0;return r>=l?!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 l=this.pendingTextureSlotRequests.get(t);l||(l=new Map,this.pendingTextureSlotRequests.set(t,l));const a={level:r,force:s,id:o,promise:i};l.set(e,a),i.finally(()=>{l.get(e)?.id===o&&l.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||oe)){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?.[q];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[q];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[q];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{width:l,height:a}=ve(e);console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[Me]=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 l=r.geometry;if(!l){g&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),f.assignLODInformation(t,l,e,s,o),f.lodInfos.set(e,i);let a=f.lowresCache.get(e)?.deref();a?a.push(r.geometry):a=[r.geometry],f.lowresCache.set(e,new WeakRef(a)),s>0&&!Y(r)&&We(r,l);for(const c of W)c.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.cacheGeneration++,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 this.disposedRequests.add(t),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||oe)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
1
|
+
import*as Ze from"three";import{RedFormat as et,RedIntegerFormat as tt,RGFormat as rt,RGIntegerFormat as st,RGBFormat as ot,RGBAFormat as nt,RGBAIntegerFormat as it,BufferGeometry as z,Mesh as V,Box3 as ae,Vector3 as P,Sphere as Pe,CompressedTexture as at,Texture as G,Matrix3 as lt,InterleavedBuffer as ut,InterleavedBufferAttribute as ct,BufferAttribute as dt,TextureLoader as ht,Color as Ce,Matrix4 as Ae}from"three";import{GLTFLoader as ge}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as gt}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as ft}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as mt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ie="";globalThis.GLTF_PROGRESSIVE_VERSION=Ie,console.debug("[gltf-progressive] version -");let E="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",X="https://cdn.needle.tools/static/three/0.179.1/basis2/";const pt=E,xt=X;if(typeof window<"u"){const n=new URL(E+"draco_decoder.js");n.searchParams.append("range","true"),fetch(n,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${E} (offline: ${navigator.onLine})`),E===pt&&Ee("./include/draco/"),X===xt&&$e("./include/ktx2/")}).finally(()=>{Be()})}const yt=()=>({dracoDecoderPath:E,ktx2TranscoderPath:X});function Ee(n){E=n,C&&C[me]!=E?(console.debug("Updating Draco decoder path to "+n),C[me]=E,C.setDecoderPath(E),C.preload()):console.debug("Setting Draco decoder path to "+n)}function $e(n){X=n,$&&$.transcoderPath!=X?(console.debug("Updating KTX2 transcoder path to "+n),$.setTranscoderPath(X),$.init()):console.debug("Setting KTX2 transcoder path to "+n)}function le(n){return Be(),n?$.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:C,ktx2Loader:$,meshoptDecoder:ue}}function fe(n){n.dracoLoader||n.setDRACOLoader(C),n.ktx2Loader||n.setKTX2Loader($),n.meshoptDecoder||n.setMeshoptDecoder(ue)}const me=Symbol("dracoDecoderPath");let C,ue,$;function Be(){C||(C=new ft,C[me]=E,C.setDecoderPath(E),C.preload()),$||($=new mt,$.setTranscoderPath(X),$.init()),ue||(ue=gt)}const pe=new WeakMap;function xe(n,t){let e=pe.get(n);e?e=Object.assign(e,t):e=t,pe.set(n,e)}const vt=ge.prototype.load;function wt(...n){const t=pe.get(this);let e=n[0];const r=Lt(e);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,vt?.call(this,...n)}ge.prototype.load=wt;function Lt(n){try{if(n instanceof URL)return n;const t=globalThis.location?.href;return t?new URL(n,t):new URL(n)}catch{return null}}function Ge(n){return n!=null&&n.data!=null}function ye(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function bt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function ve(n){const t=bt(n),e=ye(n);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}j("debugprogressive");function j(n){const t=globalThis.location?.href;if(!t)return!1;const e=new URL(t).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Mt(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 we(){if(ee!==void 0)return ee;const n=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(n),j("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function qe(){const n=globalThis.location?.href;if(!n)return!1;const t=new URL(n),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class Dt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,setTimeout(this.tick,0)}_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 _t(n){const t=n.image,e=t?.width??0,r=t?.height??0,s=t?.depth??1,o=Math.floor(Math.log2(Math.max(e,r,s)))+1,i=Tt(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function Tt(n){let t=4;const e=n.format;e===et||e===tt?t=1:e===rt||e===st?t=2:e===ot||e===1029?t=3:(e===nt||e===it)&&(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 St=typeof window>"u"&&typeof document>"u",Le=Symbol("needle:raycast-mesh");function Y(n){return n?.[Le]instanceof z?n[Le]:null}function We(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=Ot(t);e.userData={isRaycastMesh:!0},n[Le]=e}}function Ne(n=!0){if(n){if(te)return;const t=te=V.prototype.raycast;V.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(!te)return;V.prototype.raycast=te,te=null}}let te=null;function Ot(n){const t=new z;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const W=new Array,g=j("debugprogressive");let re,H=-1;if(g&&typeof window<"u"){let n=function(){H+=1,H>=t&&(H=-1),console.log(`Toggle LOD level [${H}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(H=r,console.log(`Set LOD level to [${H}]`))})}function je(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)je(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let kt=0;const Rt=we()?2:10;function Pt(n){if(se.length<Rt){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Re.createWorker(n||{});return se.push(r),r}const t=kt++%se.length;return se[t]}class Re{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){Ct(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-Cj5rV8iI.js",import.meta.url),{type:"module"});return new Re(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=yt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),s=await this._webglRenderer);const o=le(s).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const l=globalThis.location?.href;l&&(t=new URL(t,l).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(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function Ct(n){for(const t of n.geometries){const e=t.geometry,r=new z;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(be(s))}for(const s in e.attributes){const o=e.attributes[s],i=be(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>be(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ae,r.boundingBox.min=new P(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new P(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Pe(new P(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,{width:o,height:i}=ve(e);r=new at(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new G(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 lt(...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 be(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new ut(r,e.stride);t=new ct(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new dt(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const At=j("gltf-progressive-worker");j("gltf-progressive-reduce-mipmaps");const oe=j("gltf-progressive-gc"),Me=Symbol("needle-progressive-texture"),q="NEEDLE_progressive";class f{get name(){return q}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 l of t)this.getMaterialMinMaxLODsCount(l,e);return t[s]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const c=l.uniforms[a].value;c?.isTexture===!0&&i(c,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(l,a){const c=r.getAssignedLODInformation(l);if(c){const d=r.lodInfos.get(c.key);if(d&&d.lods){a.min_count=Math.min(a.min_count,d.lods.length),a.max_count=Math.max(a.max_count,d.lods.length);for(let u=0;u<d.lods.length;u++){const p=d.lods[u];p.width&&(a.lods[u]=a.lods[u]||{min_height:1/0,max_height:0},a.lods[u].min_height=Math.min(a.lods[u].min_height,p.height),a.lods[u].max_height=Math.max(a.lods[u].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 V||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const l of W)l.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(l=>{if(Array.isArray(l)){const c=o.index||0;l=l[c]}const a=t["LOD:requested level"]===e;return(a||this.shouldApplyStaleMeshLOD(t,e))&&(a&&delete t["LOD:requested level"],l&&s!=l&&(l?.isBufferGeometry?typeof r?.apply=="function"?r.apply(l,e,t):r?.apply!==!1&&(t.geometry=l):g&&console.error("Invalid LOD geometry",l))),l}).catch(l=>(console.error("Error loading mesh LOD",t,l),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 l of o.material){const a=this.assignTextureLOD(l,e,r);i.push(a)}return Promise.all(i).then(l=>{const a=new Array;for(const c of l)Array.isArray(c)&&a.push(...c);return a})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],l=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const a=o;for(const c of Object.keys(a.uniforms)){const d=a.uniforms[c].value;if(d?.isTexture===!0){const u=this.assignTextureLODForSlot(d,e,o,c,s).then(p=>(p&&a.uniforms[c].value!=p&&(a.uniforms[c].value=p,a.uniformsNeedUpdate=!0),p));i.push(u),l.push(c)}}}else for(const a of Object.keys(o)){const c=o[a];if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,a,s);i.push(d),l.push(a)}}return Promise.all(i).then(a=>{const c=new Array;for(let d=0;d<a.length;d++){const u=a[d],p=l[d];u&&u.isTexture===!0?c.push({material:o,slot:p,texture:u,level:e}):c.push({material:o,slot:p,texture:null,level:e})}return c})}if(t instanceof G||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 u=this.getPendingTextureSlotRequest(r,s);if(u&&u.level===e&&u.force===o)return u.promise}const l=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,a=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===l,c=()=>a()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),d=f.getOrLoadLOD(t,e,{isCurrent:c}).then(u=>{if(!a()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(u))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(u?.isTexture===!0){if(u!=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,u),null}this.assignTrackedTextureSlot(r,s,u)}return u}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(u=>(console.error("Error loading LOD",t,u),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,l,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 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),l=this.getAssignedLODInformation(i)?.level??1/0;return r>=l?!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 l=this.pendingTextureSlotRequests.get(t);l||(l=new Map,this.pendingTextureSlotRequests.set(t,l));const a={level:r,force:s,id:o,promise:i};l.set(e,a),i.finally(()=>{l.get(e)?.id===o&&l.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||oe)){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?.[q];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[q];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[q];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{width:l,height:a}=ve(e);console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[Me]=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 l=r.geometry;if(!l){g&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),f.assignLODInformation(t,l,e,s,o),f.lodInfos.set(e,i);let a=f.lowresCache.get(e)?.deref();a?a.push(r.geometry):a=[r.geometry],f.lowresCache.set(e,new WeakRef(a)),s>0&&!Y(r)&&We(r,l);for(const c of W)c.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.cacheGeneration++,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 this.disposedRequests.add(t),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||oe)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
2
|
${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(g||oe)&&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"||oe)&&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||oe)&&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,l){let{width:a,height:c}=ve(t);const d=a&&c?`${a}x${c}`:"N/A";let u="N/A";a&&c&&(u=`~${(_t(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} \u2014 ${t.name} ${d} (${u}), refCount: ${r} \u2192 ${l}
|
|
3
3
|
${e}`)}}static workers=[];static cacheGeneration=0;static disposedRequests=new WeakSet;static _workersIndex=0;static async getOrLoadLOD(t,e,r){const s=this.cacheGeneration,o=g=="verbose",i=this.getAssignedLODInformation(t);if(!i)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const l=i?.key;let a;if(t.isTexture===!0){const c=t;c.source&&c.source[Me]&&(a=c.source[Me])}if(a||(a=f.lodInfos.get(l)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${l}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let u=!1;const p=Array.isArray(a.lods);if(p&&e>=a.lods.length?u=!0:p||(u=!0),u){const m=this.lowresCache.get(l);if(m){const M=m.deref();if(M)return M;this.lowresCache.delete(l),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${l}`)}return null}}const c=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!c)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const d=Mt(i.url,c);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const u=d+"_"+a.guid,p=await this.tryResolveLODCacheEntry(this.cache.get(u),u,d,t,e,o);if(s!==this.cacheGeneration)return null;if(p.found)return p.value;if(r?.isCurrent?.()===!1)return o&&console.log(`Skipping stale LOD ${e} request before queue: ${d}`),null;const m=await this.queue.slot(d);if(s!==this.cacheGeneration)return null;if(r?.isCurrent?.()===!1)return o&&console.log(`Skipping stale LOD ${e} request after queue: ${d}`),null;const M=await this.tryResolveLODCacheEntry(this.cache.get(u),u,d,t,e,o);if(s!==this.cacheGeneration)return null;if(M.found)return M.value;const h=this.cache.get(u);if(h!==void 0){const D=await this.tryResolveLODCacheEntry(h,u,d,t,e,o);if(s!==this.cacheGeneration)return null;if(D.found)return D.value}if(!m.use)return g&&console.log(`LOD ${e} was aborted: ${d}`),null;const v=a,_=new Promise(async(D,L)=>{if(At){const w=await(await Pt({})).load(d);if(w.textures.length>0)for(const x of w.textures){let y=x.texture;return f.assignLODInformation(i.url,y,l,e,void 0),t instanceof G&&(y=this.copySettings(t,y)),y&&(y.guid=v.guid),D(y)}if(w.geometries.length>0){const x=new Array;for(const y of w.geometries){const R=y.geometry;f.assignLODInformation(i.url,R,l,e,y.primitiveIndex),x.push(R)}return D(x)}return D(null)}const b=new ge;fe(b),g&&(await new Promise(w=>setTimeout(w,1e3)),o&&console.warn("Start loading (delayed) "+d,v.guid));let k=d;if(v&&Array.isArray(v.lods)){const w=v.lods[e];w.hash&&(k+="?v="+w.hash)}const O=await b.loadAsync(k).catch(w=>(console.error(`Error loading LOD ${e} from ${d}
|
|
4
4
|
`,w),D(null)));if(!O)return D(null);const K=O.parser;o&&console.log("Loading finished "+d,v.guid);let I=0;if(O.parser.json.textures){let w=!1;for(const x of O.parser.json.textures){if(x?.extensions){const y=x?.extensions[q];if(y?.guid&&y.guid===v.guid){w=!0;break}}I++}if(w){let x=await K.getDependency("texture",I);return x&&f.assignLODInformation(i.url,x,l,e,void 0),o&&console.log('change "'+t.name+'" \u2192 "'+x.name+'"',d,I,x,u),t instanceof G&&(x=this.copySettings(t,x)),x&&(x.guid=v.guid),D(x)}else g&&console.warn("Could not find texture with guid",v.guid,O.parser.json)}if(I=0,O.parser.json.meshes){let w=!1;for(const x of O.parser.json.meshes){if(x?.extensions){const y=x?.extensions[q];if(y?.guid&&y.guid===v.guid){w=!0;break}}I++}if(w){const x=await K.getDependency("mesh",I);if(o&&console.log(`Loaded Mesh "${x.name}"`,d,I,x,u),x.isMesh===!0){const y=x.geometry;return f.assignLODInformation(i.url,y,l,e,0),D(y)}else{const y=new Array;for(let R=0;R<x.children.length;R++){const ne=x.children[R];if(ne.isMesh===!0){const ie=ne.geometry;f.assignLODInformation(i.url,ie,l,e,R),y.push(ie)}}return D(y)}}else g&&console.warn("Could not find mesh with guid",v.guid,O.parser.json)}return D(null)});this.cache.set(u,_),m.use(_);const T=await _;return s!==this.cacheGeneration||this.cache.get(u)!==_?null:(T!=null?T instanceof G?(this.cache.set(u,new WeakRef(T)),f._resourceRegistry.register(T,u)):Array.isArray(T)?this.cache.set(u,Promise.resolve(T)):this.cache.set(u,Promise.resolve(T)):this.cache.set(u,Promise.resolve(null)),T)}else if(t instanceof G){if(r?.isCurrent?.()===!1)return o&&console.log(`Skipping stale texture LOD ${e} request: ${d}`),null;o&&console.log("Load texture from uri: "+d);const u=await new ht().loadAsync(d);return s!==this.cacheGeneration||r?.isCurrent?.()===!1?(u?.dispose(),null):(u?(u.guid=a.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,o&&console.log(a,u)):g&&console.warn("failed loading",d),u)}}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 c=t.deref();if(c){let d=c,u=!1;if(d instanceof G&&s instanceof G?Ge(d.image)||ye(d)?d=this.copySettings(s,d):u=!0:d instanceof z&&s instanceof z&&(d.attributes.position?.array||(u=!0)),!u)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 l=await t.catch(c=>(console.error(`Error loading LOD ${o} from ${r}
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var Ke=Object.create;var Se=Object.defineProperty;var He=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Qe=Object.getPrototypeOf,Je=Object.prototype.hasOwnProperty;var Ze=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Ye(t))!Je.call(o,r)&&r!==e&&Se(o,r,{get:()=>t[r],enumerable:!(s=He(t,r))||s.enumerable});return o};var et=(o,t,e)=>(e=o!=null?Ke(Qe(o)):{},Ze(t||!o||!o.__esModule?Se(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("three"),De=require("three/examples/jsm/loaders/GLTFLoader.js"),tt=require("three/examples/jsm/libs/meshopt_decoder.module.js"),st=require("three/examples/jsm/loaders/DRACOLoader.js"),rt=require("three/examples/jsm/loaders/KTX2Loader.js");var ue=typeof document<"u"?document.currentScript:null;function nt(o){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const e in o)if(e!=="default"){const s=Object.getOwnPropertyDescriptor(o,e);Object.defineProperty(t,e,s.get?s:{enumerable:!0,get:()=>o[e]})}}return t.default=o,Object.freeze(t)}const ot=nt(f),Ge="";globalThis.GLTF_PROGRESSIVE_VERSION=Ge;console.debug("[gltf-progressive] version -");let B="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",K="https://cdn.needle.tools/static/three/0.179.1/basis2/";const it=B,at=K;if(typeof window<"u"){const o=new URL(B+"draco_decoder.js");o.searchParams.append("range","true"),fetch(o,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${B} (offline: ${navigator.onLine})`),B===it&&$e("./include/draco/"),K===at&&Be("./include/ktx2/")}).finally(()=>{qe()})}const lt=()=>({dracoDecoderPath:B,ktx2TranscoderPath:K});function $e(o){B=o,I&&I[me]!=B?(console.debug("Updating Draco decoder path to "+o),I[me]=B,I.setDecoderPath(B),I.preload()):console.debug("Setting Draco decoder path to "+o)}function Be(o){K=o,q&&q.transcoderPath!=K?(console.debug("Updating KTX2 transcoder path to "+o),q.setTranscoderPath(K),q.init()):console.debug("Setting KTX2 transcoder path to "+o)}function le(o){return qe(),o?q.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:I,ktx2Loader:q,meshoptDecoder:ae}}function Me(o){o.dracoLoader||o.setDRACOLoader(I),o.ktx2Loader||o.setKTX2Loader(q),o.meshoptDecoder||o.setMeshoptDecoder(ae)}const me=Symbol("dracoDecoderPath");let I,ae,q;function qe(){I||(I=new st.DRACOLoader,I[me]=B,I.setDecoderPath(B),I.preload()),q||(q=new rt.KTX2Loader,q.setTranscoderPath(K),q.init()),ae||(ae=tt.MeshoptDecoder)}const ye=new WeakMap;function _e(o,t){let e=ye.get(o);e?e=Object.assign(e,t):e=t,ye.set(o,e)}const ut=De.GLTFLoader.prototype.load;function ct(...o){const t=ye.get(this);let e=o[0];const s=ft(e);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,ut?.call(this,...o)}De.GLTFLoader.prototype.load=ct;function ft(o){try{if(o instanceof URL)return o;const t=globalThis.location?.href;return t?new URL(o,t):new URL(o)}catch{return null}}function Re(o){return o!=null&&o.data!=null}function xe(o){const t=o.source?.data;return t!=null&&typeof t=="object"?t:null}function dt(o){const t=o.image;return t!=null&&typeof t=="object"?t:null}function Le(o){const t=dt(o),e=xe(o);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}N("debugprogressive");function N(o){const t=globalThis.location?.href;if(!t)return!1;const s=new URL(t).searchParams.get(o);return s==null||s==="0"||s==="false"?!1:s===""?!0:s}function ht(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 Te(){if(ee!==void 0)return ee;const o=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(o),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function Fe(){const o=globalThis.location?.href;if(!o)return!1;const t=new URL(o),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class gt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}_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 pt(o){const t=o.image,e=t?.width??0,s=t?.height??0,r=t?.depth??1,n=Math.floor(Math.log2(Math.max(e,s,r)))+1,i=mt(o);return e*s*r*i*(1-Math.pow(.25,n))/(1-.25)}function mt(o){let t=4;const e=o.format;e===f.RedFormat||e===f.RedIntegerFormat?t=1:e===f.RGFormat||e===f.RGIntegerFormat?t=2:e===f.RGBFormat||e===1029?t=3:(e===f.RGBAFormat||e===f.RGBAIntegerFormat)&&(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 yt=typeof window>"u"&&typeof document>"u",we=Symbol("needle:raycast-mesh");function Z(o){return o?.[we]instanceof f.BufferGeometry?o[we]:null}function We(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!Z(o)){const s=xt(t);s.userData={isRaycastMesh:!0},o[we]=s}}function Ue(o=!0){if(o){if(te)return;const t=te=f.Mesh.prototype.raycast;f.Mesh.prototype.raycast=function(e,s){const r=this,n=Z(r);let i;n&&r.isMesh&&(i=r.geometry,r.geometry=n),t.call(this,e,s),i&&(r.geometry=i)}}else{if(!te)return;f.Mesh.prototype.raycast=te,te=null}}let te=null;function xt(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 U=new Array,p=N("debugprogressive");let ne,X=-1;if(p&&typeof window<"u"){let o=function(){X+=1,X>=t&&(X=-1),console.log(`Toggle LOD level [${X}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(ne=!ne,console.log(`Toggle wireframe [${ne}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(X=s,console.log(`Set LOD level to [${X}]`))})}function Ee(o){if(p&&ne!==void 0)if(Array.isArray(o))for(const t of o)Ee(t);else o&&"wireframe"in o&&(o.wireframe=ne===!0)}const se=new Array;let Lt=0;const wt=Te()?2:10;function vt(o){if(se.length<wt){const s=se.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const r=be.createWorker(o||{});return se.push(r),r}const t=Lt++%se.length;return se[t]}class be{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){Dt(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-Cj5rV8iI.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 be(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=lt();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),r=await this._webglRenderer);const l=le(r).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const u=globalThis.location?.href;u&&(t=new URL(t,u).toString())}const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function Dt(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(ce(r))}for(const r in e.attributes){const n=e.attributes[r],i=ce(n);s.setAttribute(r,i)}if(e.morphAttributes)for(const r in e.morphAttributes){const i=e.morphAttributes[r].map(l=>ce(l));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,{width:n,height:i}=Le(e);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 ce(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=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const re=N("gltf-progressive-gc"),fe=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",n=t[r];if(n!=null)return n;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(p==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function i(l,a){const u=s.getAssignedLODInformation(l);if(u){const h=s.lodInfos.get(u.key);if(h&&h.lods){a.min_count=Math.min(a.min_count,h.lods.length),a.max_count=Math.max(a.max_count,h.lods.length);for(let c=0;c<h.lods.length;c++){const d=h.lods[c];d.width&&(a.lods[c]=a.lods[c]||{min_height:1/0,max_height:0},a.lods[c].min_height=Math.min(a.lods[c].min_height,d.height),a.lods[c].max_height=Math.max(a.lods[c].max_height,d.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 l of U)l.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return m.getOrLoadLOD(r,e,{isCurrent:i}).then(l=>{if(Array.isArray(l)){const u=n.index||0;l=l[u]}const a=t["LOD:requested level"]===e;return(a||this.shouldApplyStaleMeshLOD(t,e))&&(a&&delete t["LOD:requested level"],l&&r!=l&&(l?.isBufferGeometry?typeof s?.apply=="function"?s.apply(l,e,t):s?.apply!==!1&&(t.geometry=l):p&&console.error("Invalid LOD geometry",l))),l}).catch(l=>(console.error("Error loading mesh LOD",t,l),null))}else p&&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 l of n.material){const a=this.assignTextureLOD(l,e,s);i.push(a)}return Promise.all(i).then(l=>{const a=new Array;for(const u of l)Array.isArray(u)&&a.push(...u);return a})}else return this.assignTextureLOD(n.material,e,s)}if(t.isMaterial===!0){const n=t,i=[],l=new Array;if(this.trackCurrentMaterialTextureSlots(n),n.uniforms&&(n.isRawShaderMaterial||n.isShaderMaterial===!0)){const a=n;for(const u of Object.keys(a.uniforms)){const h=a.uniforms[u].value;if(h?.isTexture===!0){const c=this.assignTextureLODForSlot(h,e,n,u,r).then(d=>(d&&a.uniforms[u].value!=d&&(a.uniforms[u].value=d,a.uniformsNeedUpdate=!0),d));i.push(c),l.push(u)}}}else for(const a of Object.keys(n)){const u=n[a];if(u?.isTexture===!0){const h=this.assignTextureLODForSlot(u,e,n,a,r);i.push(h),l.push(a)}}return Promise.all(i).then(a=>{const u=new Array;for(let h=0;h<a.length;h++){const c=a[h],d=l[h];c&&c.isTexture===!0?u.push({material:n,slot:d,texture:c,level:e}):u.push({material:n,slot:d,texture:null,level:e})}return u})}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){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.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 l=s&&r?this.nextTextureSlotRequestId(s,r,e,n):0,a=()=>!s||!r||this.getLatestTextureSlotRequest(s,r)?.id===l,u=()=>a()||this.shouldApplyStaleTextureSlotLOD(s,r,e,n),h=m.getOrLoadLOD(t,e,{isCurrent:u}).then(c=>{if(!a()&&!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 d=this.getMaterialTextureSlot(s,r)??t;if(d&&!n){const g=this.getAssignedLODInformation(d);if(g&&g?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",g.level,e,s,d,c),null}this.assignTrackedTextureSlot(s,r,c)}return c}else p=="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,l,h),h}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),l=this.getAssignedLODInformation(i)?.level??1/0;return s>=l?!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 l=this.pendingTextureSlotRequests.get(t);l||(l=new Map,this.pendingTextureSlotRequests.set(t,l));const a={level:s,force:r,id:n,promise:i};l.set(e,a),i.finally(()=>{l.get(e)?.id===n&&l.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)&&(p||re)){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;p&&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 p&&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){p&&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,m.registerTexture(this.url,i,r.lods?.length,s,r));n||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 n of this.parser.associations.keys())if(n.isMesh){const i=this.parser.associations.get(n);i?.meshes===s&&m.registerMesh(this.url,r.guid,n,r.lods.length,i.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,n)=>{if(!e){p&&console.error("!! gltf-progressive: Called register texture without texture");return}if(p){const{width:l,height:a}=Le(e);console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`,n,e)}e.source&&(e.source[fe]=n);const i=n.guid;m.assignLODInformation(t,e,i,s,r),m.lodInfos.set(i,n),m.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,r,n,i)=>{const l=s.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+s.name,{index:n,uuid:s.uuid},i,s),m.assignLODInformation(t,l,e,r,n),m.lodInfos.set(e,i);let u=m.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],m.lowresCache.set(e,new WeakRef(u)),r>0&&!Z(s)&&We(s,l);for(const h of U)h.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.cacheGeneration++,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 this.disposedRequests.add(t),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);(p||re)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(p||re)&&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),p==="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(p==="verbose"||re)&&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),(p||re)&&n("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),p==="verbose"&&n("[gltf-progressive] Memory: Untrack texture",r),!1;function n(i,
|
|
3
|
-
${e}`)}}static workers=[];static cacheGeneration=0;static disposedRequests=new WeakSet;static _workersIndex=0;static async getOrLoadLOD(t,e,s){const r=this.cacheGeneration,n=p=="verbose",i=this.getAssignedLODInformation(t);if(!i)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const
|
|
4
|
-
`,b),x(null)));if(!T)return x(null);const E=T.parser;n&&console.log("Loading finished "+c,v.guid);let k=0;if(T.parser.json.textures){let b=!1;for(const y of T.parser.json.textures){if(y?.extensions){const D=y?.extensions[F];if(D?.guid&&D.guid===v.guid){b=!0;break}}k++}if(b){let y=await E.getDependency("texture",k);return y&&m.assignLODInformation(i.url,y,
|
|
5
|
-
`,
|
|
1
|
+
"use strict";var Ke=Object.create;var Se=Object.defineProperty;var He=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Qe=Object.getPrototypeOf,Je=Object.prototype.hasOwnProperty;var Ze=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Ye(t))!Je.call(o,r)&&r!==e&&Se(o,r,{get:()=>t[r],enumerable:!(s=He(t,r))||s.enumerable});return o};var et=(o,t,e)=>(e=o!=null?Ke(Qe(o)):{},Ze(t||!o||!o.__esModule?Se(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("three"),De=require("three/examples/jsm/loaders/GLTFLoader.js"),tt=require("three/examples/jsm/libs/meshopt_decoder.module.js"),st=require("three/examples/jsm/loaders/DRACOLoader.js"),rt=require("three/examples/jsm/loaders/KTX2Loader.js");var le=typeof document<"u"?document.currentScript:null;function nt(o){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const e in o)if(e!=="default"){const s=Object.getOwnPropertyDescriptor(o,e);Object.defineProperty(t,e,s.get?s:{enumerable:!0,get:()=>o[e]})}}return t.default=o,Object.freeze(t)}const ot=nt(f),Ge="";globalThis.GLTF_PROGRESSIVE_VERSION=Ge;console.debug("[gltf-progressive] version -");let B="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",K="https://cdn.needle.tools/static/three/0.179.1/basis2/";const it=B,at=K;if(typeof window<"u"){const o=new URL(B+"draco_decoder.js");o.searchParams.append("range","true"),fetch(o,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${B} (offline: ${navigator.onLine})`),B===it&&$e("./include/draco/"),K===at&&Be("./include/ktx2/")}).finally(()=>{qe()})}const ut=()=>({dracoDecoderPath:B,ktx2TranscoderPath:K});function $e(o){B=o,I&&I[me]!=B?(console.debug("Updating Draco decoder path to "+o),I[me]=B,I.setDecoderPath(B),I.preload()):console.debug("Setting Draco decoder path to "+o)}function Be(o){K=o,q&&q.transcoderPath!=K?(console.debug("Updating KTX2 transcoder path to "+o),q.setTranscoderPath(K),q.init()):console.debug("Setting KTX2 transcoder path to "+o)}function ue(o){return qe(),o?q.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:I,ktx2Loader:q,meshoptDecoder:ae}}function Me(o){o.dracoLoader||o.setDRACOLoader(I),o.ktx2Loader||o.setKTX2Loader(q),o.meshoptDecoder||o.setMeshoptDecoder(ae)}const me=Symbol("dracoDecoderPath");let I,ae,q;function qe(){I||(I=new st.DRACOLoader,I[me]=B,I.setDecoderPath(B),I.preload()),q||(q=new rt.KTX2Loader,q.setTranscoderPath(K),q.init()),ae||(ae=tt.MeshoptDecoder)}const ye=new WeakMap;function _e(o,t){let e=ye.get(o);e?e=Object.assign(e,t):e=t,ye.set(o,e)}const lt=De.GLTFLoader.prototype.load;function ct(...o){const t=ye.get(this);let e=o[0];const s=ft(e);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,lt?.call(this,...o)}De.GLTFLoader.prototype.load=ct;function ft(o){try{if(o instanceof URL)return o;const t=globalThis.location?.href;return t?new URL(o,t):new URL(o)}catch{return null}}function Re(o){return o!=null&&o.data!=null}function xe(o){const t=o.source?.data;return t!=null&&typeof t=="object"?t:null}function dt(o){const t=o.image;return t!=null&&typeof t=="object"?t:null}function Le(o){const t=dt(o),e=xe(o);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}N("debugprogressive");function N(o){const t=globalThis.location?.href;if(!t)return!1;const s=new URL(t).searchParams.get(o);return s==null||s==="0"||s==="false"?!1:s===""?!0:s}function ht(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 Te(){if(ee!==void 0)return ee;const o=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(o),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function Fe(){const o=globalThis.location?.href;if(!o)return!1;const t=new URL(o),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class gt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,setTimeout(this.tick,0)}_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 pt(o){const t=o.image,e=t?.width??0,s=t?.height??0,r=t?.depth??1,n=Math.floor(Math.log2(Math.max(e,s,r)))+1,i=mt(o);return e*s*r*i*(1-Math.pow(.25,n))/(1-.25)}function mt(o){let t=4;const e=o.format;e===f.RedFormat||e===f.RedIntegerFormat?t=1:e===f.RGFormat||e===f.RGIntegerFormat?t=2:e===f.RGBFormat||e===1029?t=3:(e===f.RGBAFormat||e===f.RGBAIntegerFormat)&&(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 yt=typeof window>"u"&&typeof document>"u",we=Symbol("needle:raycast-mesh");function Z(o){return o?.[we]instanceof f.BufferGeometry?o[we]:null}function We(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!Z(o)){const s=xt(t);s.userData={isRaycastMesh:!0},o[we]=s}}function Ue(o=!0){if(o){if(te)return;const t=te=f.Mesh.prototype.raycast;f.Mesh.prototype.raycast=function(e,s){const r=this,n=Z(r);let i;n&&r.isMesh&&(i=r.geometry,r.geometry=n),t.call(this,e,s),i&&(r.geometry=i)}}else{if(!te)return;f.Mesh.prototype.raycast=te,te=null}}let te=null;function xt(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 U=new Array,p=N("debugprogressive");let ne,X=-1;if(p&&typeof window<"u"){let o=function(){X+=1,X>=t&&(X=-1),console.log(`Toggle LOD level [${X}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(ne=!ne,console.log(`Toggle wireframe [${ne}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(X=s,console.log(`Set LOD level to [${X}]`))})}function Ee(o){if(p&&ne!==void 0)if(Array.isArray(o))for(const t of o)Ee(t);else o&&"wireframe"in o&&(o.wireframe=ne===!0)}const se=new Array;let Lt=0;const wt=Te()?2:10;function vt(o){if(se.length<wt){const s=se.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const r=be.createWorker(o||{});return se.push(r),r}const t=Lt++%se.length;return se[t]}class be{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){Dt(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-Cj5rV8iI.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:le&&le.tagName.toUpperCase()==="SCRIPT"&&le.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new be(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=ut();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),r=await this._webglRenderer);const u=ue(r).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const l=globalThis.location?.href;l&&(t=new URL(t,l).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 Dt(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(ce(r))}for(const r in e.attributes){const n=e.attributes[r],i=ce(n);s.setAttribute(r,i)}if(e.morphAttributes)for(const r in e.morphAttributes){const i=e.morphAttributes[r].map(u=>ce(u));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,{width:n,height:i}=Le(e);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 ce(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=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const re=N("gltf-progressive-gc"),fe=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",n=t[r];if(n!=null)return n;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(p==="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 p&&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 h=s.lodInfos.get(l.key);if(h&&h.lods){a.min_count=Math.min(a.min_count,h.lods.length),a.max_count=Math.max(a.max_count,h.lods.length);for(let c=0;c<h.lods.length;c++){const d=h.lods[c];d.width&&(a.lods[c]=a.lods[c]||{min_height:1/0,max_height:0},a.lods[c].min_height=Math.min(a.lods[c].min_height,d.height),a.lods[c].max_height=Math.max(a.lods[c].max_height,d.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 u of U)u.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return m.getOrLoadLOD(r,e,{isCurrent:i}).then(u=>{if(Array.isArray(u)){const l=n.index||0;u=u[l]}const a=t["LOD:requested level"]===e;return(a||this.shouldApplyStaleMeshLOD(t,e))&&(a&&delete t["LOD:requested level"],u&&r!=u&&(u?.isBufferGeometry?typeof s?.apply=="function"?s.apply(u,e,t):s?.apply!==!1&&(t.geometry=u):p&&console.error("Invalid LOD geometry",u))),u}).catch(u=>(console.error("Error loading mesh LOD",t,u),null))}else p&&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 u of n.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(n.material,e,s)}if(t.isMaterial===!0){const n=t,i=[],u=new Array;if(this.trackCurrentMaterialTextureSlots(n),n.uniforms&&(n.isRawShaderMaterial||n.isShaderMaterial===!0)){const a=n;for(const l of Object.keys(a.uniforms)){const h=a.uniforms[l].value;if(h?.isTexture===!0){const c=this.assignTextureLODForSlot(h,e,n,l,r).then(d=>(d&&a.uniforms[l].value!=d&&(a.uniforms[l].value=d,a.uniformsNeedUpdate=!0),d));i.push(c),u.push(l)}}}else for(const a of Object.keys(n)){const l=n[a];if(l?.isTexture===!0){const h=this.assignTextureLODForSlot(l,e,n,a,r);i.push(h),u.push(a)}}return Promise.all(i).then(a=>{const l=new Array;for(let h=0;h<a.length;h++){const c=a[h],d=u[h];c&&c.isTexture===!0?l.push({material:n,slot:d,texture:c,level:e}):l.push({material:n,slot:d,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){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.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 u=s&&r?this.nextTextureSlotRequestId(s,r,e,n):0,a=()=>!s||!r||this.getLatestTextureSlotRequest(s,r)?.id===u,l=()=>a()||this.shouldApplyStaleTextureSlotLOD(s,r,e,n),h=m.getOrLoadLOD(t,e,{isCurrent:l}).then(c=>{if(!a()&&!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 d=this.getMaterialTextureSlot(s,r)??t;if(d&&!n){const g=this.getAssignedLODInformation(d);if(g&&g?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",g.level,e,s,d,c),null}this.assignTrackedTextureSlot(s,r,c)}return c}else p=="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,u,h),h}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),u=this.getAssignedLODInformation(i)?.level??1/0;return s>=u?!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 u=this.pendingTextureSlotRequests.get(t);u||(u=new Map,this.pendingTextureSlotRequests.set(t,u));const a={level:s,force:r,id:n,promise:i};u.set(e,a),i.finally(()=>{u.get(e)?.id===n&&u.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)&&(p||re)){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;p&&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 p&&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){p&&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,m.registerTexture(this.url,i,r.lods?.length,s,r));n||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 n of this.parser.associations.keys())if(n.isMesh){const i=this.parser.associations.get(n);i?.meshes===s&&m.registerMesh(this.url,r.guid,n,r.lods.length,i.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,n)=>{if(!e){p&&console.error("!! gltf-progressive: Called register texture without texture");return}if(p){const{width:u,height:a}=Le(e);console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${u}x${a}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`,n,e)}e.source&&(e.source[fe]=n);const i=n.guid;m.assignLODInformation(t,e,i,s,r),m.lodInfos.set(i,n),m.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,r,n,i)=>{const u=s.geometry;if(!u){p&&console.warn("gltf-progressive: Register mesh without geometry");return}u.userData||(u.userData={}),p&&console.log("> Progressive: register mesh "+s.name,{index:n,uuid:s.uuid},i,s),m.assignLODInformation(t,u,e,r,n),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&&!Z(s)&&We(s,u);for(const h of U)h.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.cacheGeneration++,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 this.disposedRequests.add(t),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);(p||re)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(p||re)&&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),p==="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(p==="verbose"||re)&&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),(p||re)&&n("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),p==="verbose"&&n("[gltf-progressive] Memory: Untrack texture",r),!1;function n(i,u){let{width:a,height:l}=Le(t);const h=a&&l?`${a}x${l}`:"N/A";let c="N/A";a&&l&&(c=`~${(pt(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} — ${t.name} ${h} (${c}), refCount: ${s} → ${u}
|
|
3
|
+
${e}`)}}static workers=[];static cacheGeneration=0;static disposedRequests=new WeakSet;static _workersIndex=0;static async getOrLoadLOD(t,e,s){const r=this.cacheGeneration,n=p=="verbose",i=this.getAssignedLODInformation(t);if(!i)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const u=i?.key;let a;if(t.isTexture===!0){const h=t;h.source&&h.source[fe]&&(a=h.source[fe])}if(a||(a=m.lodInfos.get(u)),!a)p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let d=!1;const g=Array.isArray(a.lods);if(g&&e>=a.lods.length?d=!0:g||(d=!0),d){const M=this.lowresCache.get(u);if(M){const w=M.deref();if(w)return w;this.lowresCache.delete(u),p&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${u}`)}return null}}const h=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!h)return p&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const c=ht(i.url,h);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const d=c+"_"+a.guid,g=await this.tryResolveLODCacheEntry(this.cache.get(d),d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(g.found)return g.value;if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale LOD ${e} request before queue: ${c}`),null;const M=await this.queue.slot(c);if(r!==this.cacheGeneration)return null;if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale LOD ${e} request after queue: ${c}`),null;const w=await this.tryResolveLODCacheEntry(this.cache.get(d),d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(w.found)return w.value;const L=this.cache.get(d);if(L!==void 0){const x=await this.tryResolveLODCacheEntry(L,d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(x.found)return x.value}if(!M.use)return p&&console.log(`LOD ${e} was aborted: ${c}`),null;const v=a,_=new Promise(async(x,S)=>{if(Mt){const y=await(await vt({})).load(c);if(y.textures.length>0)for(const D of y.textures){let R=D.texture;return m.assignLODInformation(i.url,R,u,e,void 0),t instanceof f.Texture&&(R=this.copySettings(t,R)),R&&(R.guid=v.guid),x(R)}if(y.geometries.length>0){const D=new Array;for(const R of y.geometries){const j=R.geometry;m.assignLODInformation(i.url,j,u,e,R.primitiveIndex),D.push(j)}return x(D)}return x(null)}const G=new De.GLTFLoader;Me(G),p&&(await new Promise(b=>setTimeout(b,1e3)),n&&console.warn("Start loading (delayed) "+c,v.guid));let P=c;if(v&&Array.isArray(v.lods)){const b=v.lods[e];b.hash&&(P+="?v="+b.hash)}const T=await G.loadAsync(P).catch(b=>(console.error(`Error loading LOD ${e} from ${c}
|
|
4
|
+
`,b),x(null)));if(!T)return x(null);const E=T.parser;n&&console.log("Loading finished "+c,v.guid);let k=0;if(T.parser.json.textures){let b=!1;for(const y of T.parser.json.textures){if(y?.extensions){const D=y?.extensions[F];if(D?.guid&&D.guid===v.guid){b=!0;break}}k++}if(b){let y=await E.getDependency("texture",k);return y&&m.assignLODInformation(i.url,y,u,e,void 0),n&&console.log('change "'+t.name+'" → "'+y.name+'"',c,k,y,d),t instanceof f.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=v.guid),x(y)}else p&&console.warn("Could not find texture with guid",v.guid,T.parser.json)}if(k=0,T.parser.json.meshes){let b=!1;for(const y of T.parser.json.meshes){if(y?.extensions){const D=y?.extensions[F];if(D?.guid&&D.guid===v.guid){b=!0;break}}k++}if(b){const y=await E.getDependency("mesh",k);if(n&&console.log(`Loaded Mesh "${y.name}"`,c,k,y,d),y.isMesh===!0){const D=y.geometry;return m.assignLODInformation(i.url,D,u,e,0),x(D)}else{const D=new Array;for(let R=0;R<y.children.length;R++){const j=y.children[R];if(j.isMesh===!0){const H=j.geometry;m.assignLODInformation(i.url,H,u,e,R),D.push(H)}}return x(D)}}else p&&console.warn("Could not find mesh with guid",v.guid,T.parser.json)}return x(null)});this.cache.set(d,_),M.use(_);const O=await _;return r!==this.cacheGeneration||this.cache.get(d)!==_?null:(O!=null?O instanceof f.Texture?(this.cache.set(d,new WeakRef(O)),m._resourceRegistry.register(O,d)):Array.isArray(O)?this.cache.set(d,Promise.resolve(O)):this.cache.set(d,Promise.resolve(O)):this.cache.set(d,Promise.resolve(null)),O)}else if(t instanceof f.Texture){if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale texture LOD ${e} request: ${c}`),null;n&&console.log("Load texture from uri: "+c);const g=await new f.TextureLoader().loadAsync(c);return r!==this.cacheGeneration||s?.isCurrent?.()===!1?(g?.dispose(),null):(g?(g.guid=a.guid,g.flipY=!1,g.needsUpdate=!0,g.colorSpace=t.colorSpace,n&&console.log(a,g)):p&&console.warn("failed loading",c),g)}}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 h=l,c=!1;if(h instanceof f.Texture&&r instanceof f.Texture?Re(h.image)||xe(h)?h=this.copySettings(r,h):c=!0:h instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(h.attributes.position?.array||(c=!0)),!c)return{found:!0,value:h}}return this.cache.delete(e),p&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let u=await t.catch(l=>(console.error(`Error loading LOD ${n} from ${s}
|
|
5
|
+
`,l),null));if(this.disposedRequests.has(t))return{found:!0,value:null};let a=!1;return u==null||(u instanceof f.Texture&&r instanceof f.Texture?Re(u.image)||xe(u)?u=this.copySettings(r,u):(a=!0,this.cache.delete(e)):u instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(u.attributes.position?.array||(a=!0,this.cache.delete(e)))),a?{found:!1}:{found:!0,value:u}}static _queue;static get queue(){return this._queue??=new gt(Te()?20:50,{debug:p!=!1})}static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const i=new _t(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?(p==="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 _t{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class oe{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){p&&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){p&&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 $=N("debugprogressive"),Tt=$==="colors",bt=N("noprogressive"),de=Symbol("Needle:LODSManager"),he=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),Ce=ot,C={mesh_lod:-1,texture_lod:-1},Ot=new f.Color,ve=[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];function St(){const o=Ce.Timer||Ce.Clock;return new o}const ge=new f.Box3,z=new f.Box3,Pe=new f.Box3,Rt=new f.Vector3,Ct=new f.Vector3,Pt=new f.Matrix4,W=new f.Vector3,Y=new f.Vector3,Q=new f.Vector3,J=new f.Vector3;function kt(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 Ve(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:l=-1,xrEnabled:a=!1,debugDrawLine:u,warnMissingPrimitiveDensities:h=!1}=o,c=m.getMeshLODExtension(t)?.lods,d=m.getPrimitiveIndex(t),g=o.target??{level:l,primitiveIndex:d,screenCoverage:0,screenspaceVolume:new f.Vector3,centrality:1};g.level=l,g.primitiveIndex=d,g.screenCoverage=0,g.screenspaceVolume.set(0,0,0),g.centrality=1;let M=o.boundingBox??t.boundingBox;if(M||(t.computeBoundingBox(),M=t.boundingBox),!M)return g;if(ge.copy(M).applyMatrix4(e),s.isPerspectiveCamera&&kt(ge,r))return g.level=0,g.screenCoverage=1/0,g.screenspaceVolume.set(1/0,1/0,1/0),g;if(z.copy(ge).applyMatrix4(r),a&&s.isPerspectiveCamera&&s.fov>70){const x=z.min,S=z.max;let G=x.x,P=x.y,T=S.x,E=S.y;const k=2,b=1.5,y=(x.x+S.x)*.5,D=(x.y+S.y)*.5;G=(G-y)*k+y,P=(P-D)*k+D,T=(T-y)*k+y,E=(E-D)*k+D;const R=G<0&&T>0?0:Math.min(Math.abs(x.x),Math.abs(S.x)),j=P<0&&E>0?0:Math.min(Math.abs(x.y),Math.abs(S.y)),H=Math.max(R,j);g.centrality=(b-H)*(b-H)*(b-H)}const w=z.getSize(Rt);w.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&w.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(w.x*=s.aspect),Pe.copy(M).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Pe.getSize(Ct),v=Math.max(w.x,w.y),_=Math.max(L.x,L.y);v!==0&&_!==0&&(w.z=L.z/_*v);const O=Math.max(w.x,w.y,w.z)*g.centrality;if(g.screenCoverage=O,g.screenspaceVolume.copy(w),O<=0)return g;if(u){const x=Pt.copy(r);x.invert(),W.copy(z.min),Y.copy(z.max),Y.x=W.x,Q.copy(z.max),Q.y=W.y,J.copy(z.max);const S=(W.z+J.z)*.5;W.z=Y.z=Q.z=J.z=S,W.applyMatrix4(x),Y.applyMatrix4(x),Q.applyMatrix4(x),J.applyMatrix4(x),u(W,Y,255),u(W,Q,255),u(Y,J,255),u(Q,J,255)}if(c?.length)for(let x=0;x<c.length;x++){const S=c[x],G=S.densities?.[d]||S.density||1e-5;if(d>0&&h&&Fe()&&!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.")),G/O<n){g.level=x;break}}return g}class A{static debugDrawLine;static getObjectLODState(t){return t[he]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static getPlugins(){return U}static get(t,e){if(t[de])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[de];const s=new A(t,{engine:"unknown",...e});return t[de]=s,s}renderer;context;projectionScreenMatrix=new f.Matrix4;get plugins(){return U}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 oe(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),Fe()&&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 oe.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;#r=0;#n=0;#i=0;#s=0;#o=St();_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;le(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.#o.update?.(),e.#n=Math.max(e.#o.getDelta(),1/1e3),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,$&&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 r=this.getRenderList(t,e,s);if(!r)return;const n=r.opaque;let i=!0;if(n.length===1){const l=n[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(bt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,$&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,$&&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.getRenderList(t,e,0);if(!s)return;const r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=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.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 u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const i=s.transparent;for(const a of i){const u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}}getRenderList(t,e,s){const r=this.renderer;let n=null;if(r.isWebGPURenderer===!0){const i=r._renderLists;if(!i)return null;n=i.get(t,e)}else if(r.isWebGLRenderer===!0){const i=r.renderLists;if(!i)return null;n=i.get(t,s)}return n?{opaque:n.opaque||[],transparent:n.transparent||[],transmissive:n.transmissive||n.transparentDoublePass||[],transparentDoublePass:n.transparentDoublePass||[]}:null}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[he];if(n||(n=new At,s[he]=n),n.frames++<2)return;for(const l of U)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:X;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),p&&s.material&&!s.isGizmo&&Ee(s.material),Tt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&ze(s.material,C.mesh_lod);for(const l of U)l.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[V]===void 0||e<t[V])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[V]!=s,e=s),r){t[V]=e;const i=n?{force:!0}:void 0,l=m.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});oe.addPromise("texture",t,l,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[V]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[V]!=r,e=r),s){t[V]=e;const n=t.geometry,i=m.assignMeshLOD(t,e).then(l=>(l&&t[V]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return oe.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 l=10+1,a=!1;if($&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,h=m.getPrimitiveIndex(e.geometry),c=u&&u.length>0,d=m.getMaterialMinMaxLODsCount(e.material),g=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!g){n.mesh_lod=0,n.texture_lod=0;return}c||(a=!0,l=0);const M=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!L[A.$skinnedMeshBoundsOffset]){const _=A.skinnedMeshBoundsFrameOffsetCounter++;L[A.$skinnedMeshBoundsOffset]=_}const v=L[A.$skinnedMeshBoundsOffset];if((s.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const _=Z(L),O=L.geometry;_&&(L.geometry=_),L.computeBoundingBox(),L.geometry=O}}w=L.boundingBox}if(w){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 _=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(_)){n.mesh_lod=0,n.texture_lod=0;return}}const L=Ve({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:M,currentLevel:s.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:$?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=L.centrality,s.lastScreenCoverage=L.screenCoverage,s.lastScreenspaceVolume.copy(L.screenspaceVolume),L.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}L.level>=0&&L.level<l&&(l=L.level,a=!0)}if(a?n.mesh_lod=l:n.mesh_lod=s.lastLodLevel_Mesh,$&&n.mesh_lod!=s.lastLodLevel_Mesh){const v=u?.[n.mesh_lod];v&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${v.densities?.[h].toFixed(0)}) | ${e.name}`)}if(g){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=d.max_count-1,$){const v=d.lods[d.max_count-1];$&&console.log(`First Texture LOD ${n.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let _=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(_*=1.5);const O=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,S=M/O*_;let G=!1;for(let P=d.lods.length-1;P>=0;P--){const T=d.lods[P];if(!(L&&T.max_height>=2048)&&!(Te()&&T.max_height>4096)&&(T.max_height>S||!G&&P===0)){if(G=!0,n.texture_lod=P,$&&n.texture_lod<s.lastLodLevel_Texture){const E=T.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${E}px
|
|
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 _t{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class oe{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){p&&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){p&&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 $=N("debugprogressive"),Tt=$==="colors",bt=N("noprogressive"),de=Symbol("Needle:LODSManager"),he=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),Ce=ot,C={mesh_lod:-1,texture_lod:-1},Ot=new f.Color,ve=[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];function St(){const o=Ce.Timer||Ce.Clock;return new o}const ge=new f.Box3,z=new f.Box3,Pe=new f.Box3,Rt=new f.Vector3,Ct=new f.Vector3,Pt=new f.Matrix4,W=new f.Vector3,Y=new f.Vector3,Q=new f.Vector3,J=new f.Vector3;function kt(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 Ve(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:u=-1,xrEnabled:a=!1,debugDrawLine:l,warnMissingPrimitiveDensities:h=!1}=o,c=m.getMeshLODExtension(t)?.lods,d=m.getPrimitiveIndex(t),g=o.target??{level:u,primitiveIndex:d,screenCoverage:0,screenspaceVolume:new f.Vector3,centrality:1};g.level=u,g.primitiveIndex=d,g.screenCoverage=0,g.screenspaceVolume.set(0,0,0),g.centrality=1;let M=o.boundingBox??t.boundingBox;if(M||(t.computeBoundingBox(),M=t.boundingBox),!M)return g;if(ge.copy(M).applyMatrix4(e),s.isPerspectiveCamera&&kt(ge,r))return g.level=0,g.screenCoverage=1/0,g.screenspaceVolume.set(1/0,1/0,1/0),g;if(z.copy(ge).applyMatrix4(r),a&&s.isPerspectiveCamera&&s.fov>70){const x=z.min,S=z.max;let G=x.x,P=x.y,T=S.x,E=S.y;const k=2,b=1.5,y=(x.x+S.x)*.5,D=(x.y+S.y)*.5;G=(G-y)*k+y,P=(P-D)*k+D,T=(T-y)*k+y,E=(E-D)*k+D;const R=G<0&&T>0?0:Math.min(Math.abs(x.x),Math.abs(S.x)),j=P<0&&E>0?0:Math.min(Math.abs(x.y),Math.abs(S.y)),H=Math.max(R,j);g.centrality=(b-H)*(b-H)*(b-H)}const w=z.getSize(Rt);w.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&w.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(w.x*=s.aspect),Pe.copy(M).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Pe.getSize(Ct),v=Math.max(w.x,w.y),_=Math.max(L.x,L.y);v!==0&&_!==0&&(w.z=L.z/_*v);const O=Math.max(w.x,w.y,w.z)*g.centrality;if(g.screenCoverage=O,g.screenspaceVolume.copy(w),O<=0)return g;if(l){const x=Pt.copy(r);x.invert(),W.copy(z.min),Y.copy(z.max),Y.x=W.x,Q.copy(z.max),Q.y=W.y,J.copy(z.max);const S=(W.z+J.z)*.5;W.z=Y.z=Q.z=J.z=S,W.applyMatrix4(x),Y.applyMatrix4(x),Q.applyMatrix4(x),J.applyMatrix4(x),l(W,Y,255),l(W,Q,255),l(Y,J,255),l(Q,J,255)}if(c?.length)for(let x=0;x<c.length;x++){const S=c[x],G=S.densities?.[d]||S.density||1e-5;if(d>0&&h&&Fe()&&!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.")),G/O<n){g.level=x;break}}return g}class A{static debugDrawLine;static getObjectLODState(t){return t[he]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static getPlugins(){return U}static get(t,e){if(t[de])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[de];const s=new A(t,{engine:"unknown",...e});return t[de]=s,s}renderer;context;projectionScreenMatrix=new f.Matrix4;get plugins(){return U}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 oe(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),Fe()&&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 oe.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;#r=0;#n=0;#i=0;#s=0;#o=St();_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;ue(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.#o.update?.(),e.#n=Math.max(e.#o.getDelta(),1/1e3),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((u,a)=>u+a)/e._fpsBuffer.length,$&&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 r=this.getRenderList(t,e,s);if(!r)return;const n=r.opaque;let i=!0;if(n.length===1){const u=n[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(bt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,$&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,$&&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.getRenderList(t,e,0);if(!s)return;const r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=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.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 f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const i=s.transparent;for(const a of i){const l=a.object;(l instanceof f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const u=s.transmissive;for(const a of u){const l=a.object;(l instanceof f.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}}getRenderList(t,e,s){const r=this.renderer;let n=null;if(r.isWebGPURenderer===!0){const i=r._renderLists;if(!i)return null;n=i.get(t,e)}else if(r.isWebGLRenderer===!0){const i=r.renderLists;if(!i)return null;n=i.get(t,s)}return n?{opaque:n.opaque||[],transparent:n.transparent||[],transmissive:n.transmissive||n.transparentDoublePass||[],transparentDoublePass:n.transparentDoublePass||[]}:null}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[he];if(n||(n=new At,s[he]=n),n.frames++<2)return;for(const u of U)u.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:X;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),p&&s.material&&!s.isGizmo&&Ee(s.material),Tt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&ze(s.material,C.mesh_lod);for(const u of U)u.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[V]===void 0||e<t[V])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[V]!=s,e=s),r){t[V]=e;const i=n?{force:!0}:void 0,u=m.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});oe.addPromise("texture",t,u,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[V]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[V]!=r,e=r),s){t[V]=e;const n=t.geometry,i=m.assignMeshLOD(t,e).then(u=>(u&&t[V]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),u));return oe.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 u=10+1,a=!1;if($&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=m.getMeshLODExtension(e.geometry)?.lods,h=m.getPrimitiveIndex(e.geometry),c=l&&l.length>0,d=m.getMaterialMinMaxLODsCount(e.material),g=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!g){n.mesh_lod=0,n.texture_lod=0;return}c||(a=!0,u=0);const M=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!L[A.$skinnedMeshBoundsOffset]){const _=A.skinnedMeshBoundsFrameOffsetCounter++;L[A.$skinnedMeshBoundsOffset]=_}const v=L[A.$skinnedMeshBoundsOffset];if((s.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const _=Z(L),O=L.geometry;_&&(L.geometry=_),L.computeBoundingBox(),L.geometry=O}}w=L.boundingBox}if(w){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 _=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(_)){n.mesh_lod=0,n.texture_lod=0;return}}const L=Ve({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:M,currentLevel:s.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:$?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=L.centrality,s.lastScreenCoverage=L.screenCoverage,s.lastScreenspaceVolume.copy(L.screenspaceVolume),L.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}L.level>=0&&L.level<u&&(u=L.level,a=!0)}if(a?n.mesh_lod=u:n.mesh_lod=s.lastLodLevel_Mesh,$&&n.mesh_lod!=s.lastLodLevel_Mesh){const v=l?.[n.mesh_lod];v&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${v.densities?.[h].toFixed(0)}) | ${e.name}`)}if(g){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=d.max_count-1,$){const v=d.lods[d.max_count-1];$&&console.log(`First Texture LOD ${n.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let _=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(_*=1.5);const O=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,S=M/O*_;let G=!1;for(let P=d.lods.length-1;P>=0;P--){const T=d.lods[P];if(!(L&&T.max_height>=2048)&&!(Te()&&T.max_height>4096)&&(T.max_height>S||!G&&P===0)){if(G=!0,n.texture_lod=P,$&&n.texture_lod<s.lastLodLevel_Texture){const E=T.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${E}px
|
|
8
8
|
Screensize: ${S.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${v.toFixed(1)}
|
|
9
9
|
${e.name}`)}break}}}}else n.texture_lod=0}}class At{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new f.Vector3;lastCentrality=0}function ze(o,t){if(!(t<0)){if(Array.isArray(o)){for(const e of o)ze(e,t);return}"color"in o&&o.color instanceof f.Color&&(o.color.copy(Ne(t,Ot)),o.needsUpdate=!0)}}function Ne(o,t){const e=Math.max(0,Math.min(ve.length-1,Math.floor(o)));return t.setHex(ve[e])}const ke=Symbol("NEEDLE_mesh_lod"),ie=Symbol("NEEDLE_texture_lod");let pe=null;function Oe(){const o=It();o&&(o.mapURLs(function(t){return Ae(),t}),Ae(),pe?.disconnect(),pe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&je(s)})})}),pe.observe(document,{childList:!0,subtree:!0}))}function It(){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"),Oe()}),null)}function Ae(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{je(t)})}const Ie=new WeakSet;let Gt=0;function je(o){if(!o||Ie.has(o))return null;Ie.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++Gt+`
|
|
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(
|
|
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)"),u=n.find(l=>l.toString()=="Symbol(scene)"),a=n.find(l=>l.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&u!=null&&(e=o[u]),!s&&a!=null&&(s=o[a])}if(t&&e){let r=function(){if(s){let i=0;const u=setInterval(()=>{if(i++>5){clearInterval(u);return}s?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=A.get(t,{engine:"model-viewer"});return A.addPlugin(new $t),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 $t{_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[ie]==!0)return;e[ie]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(n&&s&&e.material){let i=function(a){if(a[ie]==!0)return;a[ie]=!0,a.userData&&(a.userData.LOD=-1);const l=Object.keys(a);for(let h=0;h<l.length;h++){const c=l[h],d=a[c];if(d?.isTexture===!0){const g=d.userData?.associations?.textures;if(g==null)continue;const M=s.parser.json.textures[g];if(!M){console.warn("Texture data not found for texture index "+g);continue}if(M?.extensions?.[F]){const w=M.extensions[F];w&&n&&m.registerTexture(n,d,w.lods.length,g,w)}}}};const u=e.material;if(Array.isArray(u))for(const a of u)i(a);else i(u)}}tryParseMeshLOD(t,e){if(e[ke]==!0)return;e[ke]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const n=e.userData?.gltfExtensions?.[F];if(n&&r){const i=e.uuid;m.registerMesh(r,i,e,0,n.lods.length,n)}}}function Xe(...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")}ue(e),Me(s),_e(s,{progressive:!0,...r?.hints}),s.register(i=>new m(i));const n=A.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}Oe();if(!yt){const o={gltfProgressive:{useNeedleProgressive:Xe,LODsManager:A,configureLoader:_e,getRaycastMesh:Z,useRaycastMeshes:Ue}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=F;exports.LODsManager=A;exports.NEEDLE_progressive=m;exports.VERSION=Ge;exports.addDracoAndKTX2Loaders=Me;exports.calculateMeshLODLevel=Ve;exports.configureLoader=_e;exports.createLoaders=ue;exports.getLODColor=Ne;exports.getRaycastMesh=Z;exports.lodDebugColors=ve;exports.patchModelViewer=Oe;exports.registerRaycastMesh=We;exports.setDracoDecoderLocation=$e;exports.setKTX2TranscoderLocation=Be;exports.useNeedleProgressive=Xe;exports.useRaycastMeshes=Ue;
|
package/lib/utils.internal.js
CHANGED
|
@@ -106,14 +106,9 @@ export class PromiseQueue {
|
|
|
106
106
|
constructor(maxConcurrent, opts = {}) {
|
|
107
107
|
this.maxConcurrent = maxConcurrent;
|
|
108
108
|
this.debug = opts.debug ?? false;
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
globalThis.requestAnimationFrame(this.tick);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
setTimeout(this.tick, 0);
|
|
116
|
-
}
|
|
109
|
+
// Start with a timer, not requestAnimationFrame: window rAF does not fire while an immersive WebXR session
|
|
110
|
+
// is presenting (e.g. a page reloaded straight into VR via sessiongranted), so the queue would never tick.
|
|
111
|
+
setTimeout(this.tick, 0);
|
|
117
112
|
}
|
|
118
113
|
tick = () => {
|
|
119
114
|
this.internalUpdate();
|
package/lib/version.js
CHANGED
package/package.json
CHANGED