@needle-tools/gltf-progressive 3.6.1 → 3.6.2
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 +188 -181
- package/gltf-progressive.min.js +8 -8
- package/gltf-progressive.umd.cjs +8 -8
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/loaders.d.ts +2 -0
- package/lib/loaders.js +17 -4
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import*as et from"three";import{RedFormat as tt,RedIntegerFormat as rt,RGFormat as st,RGIntegerFormat as ot,RGBFormat as nt,RGBAFormat as it,RGBAIntegerFormat as at,BufferGeometry as z,Mesh as V,Box3 as ae,Vector3 as C,Sphere as Pe,CompressedTexture as lt,Texture as G,Matrix3 as ut,InterleavedBuffer as ct,InterleavedBufferAttribute as dt,BufferAttribute as ht,TextureLoader as gt,Color as Ce,Matrix4 as Ae}from"three";import{GLTFLoader as ge}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as ft}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as mt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as pt}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 xt=E,yt=X,Ee=new URL(E+"draco_decoder.js");Ee.searchParams.append("range","true"),fetch(Ee,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${E} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),E===xt&&$e("./include/draco/"),X===yt&&Be("./include/ktx2/")}).finally(()=>{Ge()});const vt=()=>({dracoDecoderPath:E,ktx2TranscoderPath:X});function $e(n){E=n,R&&R[me]!=E?(console.debug("Updating Draco decoder path to "+n),R[me]=E,R.setDecoderPath(E),R.preload()):console.debug("Setting Draco decoder path to "+n)}function Be(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 Ge(),n?$.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:R,ktx2Loader:$,meshoptDecoder:ue}}function fe(n){n.dracoLoader||n.setDRACOLoader(R),n.ktx2Loader||n.setKTX2Loader($),n.meshoptDecoder||n.setMeshoptDecoder(ue)}const me=Symbol("dracoDecoderPath");let R,ue,$;function Ge(){R||(R=new mt,R[me]=E,R.setDecoderPath(E),R.setDecoderConfig({type:"js"}),R.preload()),$||($=new pt,$.setTranscoderPath(X),$.init()),ue||(ue=ft)}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 wt=ge.prototype.load;function Lt(...n){const t=pe.get(this);let e=n[0];const r=bt(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,wt?.call(this,...n)}ge.prototype.load=Lt;function bt(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 qe(n){return n!=null&&n.data!=null}function ye(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function Mt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function ve(n){const t=Mt(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 Dt(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function 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 We(){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 _t{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 Tt(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=St(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function St(n){let t=4;const e=n.format;e===tt||e===rt?t=1:e===st||e===ot?t=2:e===nt||e===1029?t=3:(e===it||e===at)&&(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 Ot=typeof window>"u"&&typeof document>"u",Le=Symbol("needle:raycast-mesh");function Y(n){return n?.[Le]instanceof z?n[Le]:null}function Ne(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=kt(t);e.userData={isRaycastMesh:!0},n[Le]=e}}function je(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 kt(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 Ue(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)Ue(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let Rt=0;const Pt=we()?2:10;function Ct(n){if(se.length<Pt){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Re.createWorker(n||{});return se.push(r),r}const t=Rt++%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){At(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Re(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=vt();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 At(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 C(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new C(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Pe(new C(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 lt(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 ut(...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 ct(r,e.stride);t=new dt(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new ht(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const It=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)&&Ne(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
|
-
${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}=
|
|
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[
|
|
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 P=0;P<x.children.length;P++){const ne=x.children[P];if(ne.isMesh===!0){const ie=ne.geometry;f.assignLODInformation(i.url,ie,l,e,P),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
|
|
5
|
-
`,c),null));if(this.disposedRequests.has(t))return{found:!0,value:null};let a=!1;return l==null||(l instanceof G&&s instanceof G?
|
|
1
|
+
import*as tt from"three";import{RedFormat as rt,RedIntegerFormat as st,RGFormat as ot,RGIntegerFormat as nt,RGBFormat as it,RGBAFormat as at,RGBAIntegerFormat as lt,BufferGeometry as z,Mesh as V,Box3 as ae,Vector3 as C,Sphere as Ae,CompressedTexture as ut,Texture as G,Matrix3 as ct,InterleavedBuffer as dt,InterleavedBufferAttribute as ht,BufferAttribute as gt,TextureLoader as ft,Color as Ie,Matrix4 as Ee}from"three";import{GLTFLoader as he}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as mt}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as pt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as xt}from"three/examples/jsm/loaders/KTX2Loader.js";const $e="";globalThis.GLTF_PROGRESSIVE_VERSION=$e,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 yt=E,vt=X,Be=new URL(E+"draco_decoder.js");Be.searchParams.append("range","true"),fetch(Be,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${E} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),E===yt&&Ge("./include/draco/"),X===vt&&qe("./include/ktx2/")}).finally(()=>{me&&xe()});const wt=()=>({dracoDecoderPath:E,ktx2TranscoderPath:X});function Ge(n){E=n,k&&k[fe]!=E?(console.debug("Updating Draco decoder path to "+n),k[fe]=E,k.setDecoderPath(E),k.preload()):console.debug("Setting Draco decoder path to "+n)}function qe(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){me=!0;const t=xe();return n?t.ktx2Loader.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),t}function Lt(){me=!1,k?.dispose(),$?.dispose(),k=void 0,$=void 0}function ge(n){const{dracoLoader:t,ktx2Loader:e,meshoptDecoder:r}=xe();n.dracoLoader||n.setDRACOLoader(t),n.ktx2Loader||n.setKTX2Loader(e),n.meshoptDecoder||n.setMeshoptDecoder(r)}const fe=Symbol("dracoDecoderPath");let me=!0,k,pe,$;function xe(){return k||(k=new pt,k[fe]=E,k.setDecoderPath(E),k.setDecoderConfig({type:"js"}),k.preload()),$||($=new xt,$.setTranscoderPath(X),$.init()),pe||(pe=mt),{dracoLoader:k,ktx2Loader:$,meshoptDecoder:pe}}const ye=new WeakMap;function ve(n,t){let e=ye.get(n);e?e=Object.assign(e,t):e=t,ye.set(n,e)}const bt=he.prototype.load;function Mt(...n){const t=ye.get(this);let e=n[0];const r=Dt(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,bt?.call(this,...n)}he.prototype.load=Mt;function Dt(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 We(n){return n!=null&&n.data!=null}function we(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function _t(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function Le(n){const t=_t(n),e=we(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 Tt(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 be(){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 Ne(){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 St{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 Ot(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=kt(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function kt(n){let t=4;const e=n.format;e===rt||e===st?t=1:e===ot||e===nt?t=2:e===it||e===1029?t=3:(e===at||e===lt)&&(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 Rt=typeof window>"u"&&typeof document>"u",Me=Symbol("needle:raycast-mesh");function Y(n){return n?.[Me]instanceof z?n[Me]:null}function je(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=Pt(t);e.userData={isRaycastMesh:!0},n[Me]=e}}function Ue(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 Pt(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 Fe(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)Fe(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let Ct=0;const At=be()?2:10;function It(n){if(se.length<At){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Ce.createWorker(n||{});return se.push(r),r}const t=Ct++%se.length;return se[t]}class Ce{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){Et(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Ce(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=wt();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 Et(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(De(s))}for(const s in e.attributes){const o=e.attributes[s],i=De(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>De(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ae,r.boundingBox.min=new C(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new C(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Ae(new C(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}=Le(e);r=new ut(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 ct(...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 De(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new dt(r,e.stride);t=new ht(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new gt(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const $t=j("gltf-progressive-worker");j("gltf-progressive-reduce-mipmaps");const oe=j("gltf-progressive-gc"),_e=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}=Le(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[_e]=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)&&je(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
|
+
${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}=Le(t);const d=a&&c?`${a}x${c}`:"N/A";let u="N/A";a&&c&&(u=`~${(Ot(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} \u2014 ${t.name} ${d} (${u}), refCount: ${r} \u2192 ${l}
|
|
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[_e]&&(a=c.source[_e])}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=Tt(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($t){const w=await(await It({})).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 P=y.geometry;f.assignLODInformation(i.url,P,l,e,y.primitiveIndex),x.push(P)}return D(x)}return D(null)}const b=new he;ge(b),g&&(await new Promise(w=>setTimeout(w,1e3)),o&&console.warn("Start loading (delayed) "+d,v.guid));let R=d;if(v&&Array.isArray(v.lods)){const w=v.lods[e];w.hash&&(R+="?v="+w.hash)}const O=await b.loadAsync(R).catch(w=>(console.error(`Error loading LOD ${e} from ${d}
|
|
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 P=0;P<x.children.length;P++){const ne=x.children[P];if(ne.isMesh===!0){const ie=ne.geometry;f.assignLODInformation(i.url,ie,l,e,P),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 ft().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?We(d.image)||we(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}
|
|
5
|
+
`,c),null));if(this.disposedRequests.has(t))return{found:!0,value:null};let a=!1;return l==null||(l instanceof G&&s instanceof G?We(l.image)||we(l)?l=this.copySettings(s,l):(a=!0,this.cache.delete(e)):l instanceof z&&s instanceof z&&(l.attributes.position?.array||(a=!0,this.cache.delete(e)))),a?{found:!1}:{found:!0,value:l}}static _queue;static get queue(){return this._queue??=new St(be()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,r,s,o){if(!e)return;e.userData||(e.userData={});const i=new Bt(t,r,s,o);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
|
|
6
6
|
`,t.uuid,`
|
|
7
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class Et{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class ce{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const B=j("debugprogressive"),$t=B==="colors",Bt=j("noprogressive"),De=Symbol("Needle:LODSManager"),_e=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),Fe=et,S={mesh_lod:-1,texture_lod:-1},Gt=new Ce,Te=[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 qt(){const n=Fe.Timer||Fe.Clock;return new n}const Se=new ae,F=new ae,ze=new ae,Wt=new C,Nt=new C,jt=new Ae,N=new C,Q=new C,J=new C,Z=new C;function Ut(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return N.set(s,o,e.z).applyMatrix4(t).z<0}function Ve(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:l=-1,xrEnabled:a=!1,debugDrawLine:c,warnMissingPrimitiveDensities:d=!1}=n,u=f.getMeshLODExtension(t)?.lods,p=f.getPrimitiveIndex(t),m=n.target??{level:l,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new C,centrality:1};m.level=l,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1;let M=n.boundingBox??t.boundingBox;if(M||(t.computeBoundingBox(),M=t.boundingBox),!M)return m;if(Se.copy(M).applyMatrix4(e),r.isPerspectiveCamera&&Ut(Se,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(F.copy(Se).applyMatrix4(s),a&&r.isPerspectiveCamera&&r.fov>70){const L=F.min,b=F.max;let k=L.x,O=L.y,K=b.x,I=b.y;const w=2,x=1.5,y=(L.x+b.x)*.5,P=(L.y+b.y)*.5;k=(k-y)*w+y,O=(O-P)*w+P,K=(K-y)*w+y,I=(I-P)*w+P;const ne=k<0&&K>0?0:Math.min(Math.abs(L.x),Math.abs(b.x)),ie=O<0&&I>0?0:Math.min(Math.abs(L.y),Math.abs(b.y)),he=Math.max(ne,ie);m.centrality=(x-he)*(x-he)*(x-he)}const h=F.getSize(Wt);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),ze.copy(M).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const v=ze.getSize(Nt),_=Math.max(h.x,h.y),T=Math.max(v.x,v.y);_!==0&&T!==0&&(h.z=v.z/T*_);const D=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=D,m.screenspaceVolume.copy(h),D<=0)return m;if(c){const L=jt.copy(s);L.invert(),N.copy(F.min),Q.copy(F.max),Q.x=N.x,J.copy(F.max),J.y=N.y,Z.copy(F.max);const b=(N.z+Z.z)*.5;N.z=Q.z=J.z=Z.z=b,N.applyMatrix4(L),Q.applyMatrix4(L),J.applyMatrix4(L),Z.applyMatrix4(L),c(N,Q,255),c(N,J,255),c(Q,Z,255),c(J,Z,255)}if(u?.length)for(let L=0;L<u.length;L++){const b=u[L],k=b.densities?.[p]||b.density||1e-5;if(p>0&&d&&We()&&!b.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.")),k/D<o){m.level=L;break}}return m}class A{static debugDrawLine;static getObjectLODState(t){return t[_e]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static getPlugins(){return W}static get(t,e){if(t[De])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[De];const r=new A(t,{engine:"unknown",...e});return t[De]=r,r}renderer;context;projectionScreenMatrix=new Ae;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new ce(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),We()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return ce.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#s=0;#o=0;#i=0;#r=0;#n=qt();_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(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n.update?.(),e.#o=Math.max(e.#n.getDelta(),1/1e3),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.getRenderList(t,e,r);if(!s)return;const o=s.opaque;let i=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Bt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.getRenderList(t,e,0);if(!r)return;const s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of s){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){B&&(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 c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}const i=r.transparent;for(const a of i){const c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}const l=r.transmissive;for(const a of l){const c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}}getRenderList(t,e,r){const s=this.renderer;let o=null;if(s.isWebGPURenderer===!0){const i=s._renderLists;if(!i)return null;o=i.get(t,e)}else if(s.isWebGLRenderer===!0){const i=s.renderLists;if(!i)return null;o=i.get(t,r)}return o?{opaque:o.opaque||[],transparent:o.transparent||[],transmissive:o.transmissive||o.transparentDoublePass||[],transparentDoublePass:o.transparentDoublePass||[]}:null}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[_e];if(o||(o=new Ft,r[_e]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:H;i>=0?(S.mesh_lod=i,S.texture_lod=i):(this.calculateLodLevel(e,r,o,s,S),S.mesh_lod=Math.round(S.mesh_lod),S.texture_lod=Math.round(S.texture_lod)),S.mesh_lod>=0&&this.loadProgressiveMeshes(r,S.mesh_lod),r.material&&S.texture_lod>=0&&this.loadProgressiveTextures(r.material,S.texture_lod,i),g&&r.material&&!r.isGizmo&&Ue(r.material),$t&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&Xe(r.material,S.mesh_lod);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,S);o.lastLodLevel_Mesh=S.mesh_lod,o.lastLodLevel_Texture=S.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[U]===void 0||e<t[U])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[U]!=r,e=r),s){t[U]=e;const i=o?{force:!0}:void 0,l=f.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(c=>c({type:"texture",level:e,object:t}))});ce.addPromise("texture",t,l,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[U]!=s,e=s),r){t[U]=e;const o=t.geometry,i=f.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ce.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new Pe;_tempWorldPosition=new C;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=f.getMeshLODExtension(e.geometry)?.lods,c=f.getPrimitiveIndex(e.geometry),d=a&&a.length>0,u=f.getMaterialMinMaxLODsCount(e.material),p=u.min_count!==1/0&&u.min_count>=0&&u.max_count>=0;if(!d&&!p){o.mesh_lod=0,o.texture_lod=0;return}d||(l=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[A.$skinnedMeshBoundsOffset]){const _=A.skinnedMeshBoundsFrameOffsetCounter++;h[A.$skinnedMeshBoundsOffset]=_}const v=h[A.$skinnedMeshBoundsOffset];if((r.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const _=Y(h),T=h.geometry;_&&(h.geometry=_),h.computeBoundingBox(),h.geometry=T}}M=h.boundingBox}if(M){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const v=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(v)){o.mesh_lod=0,o.texture_lod=0;return}}const h=Ve({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:M,xrEnabled:this.renderer.xr.enabled,debugDrawLine:B?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,l=!0)}if(l?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,B&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=a?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[c].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=u.max_count-1,B){const v=u.lods[u.max_count-1];B&&console.log(`First Texture LOD ${o.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let _=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(_*=1.5);const T=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,D=m/T*_;let L=!1;for(let b=u.lods.length-1;b>=0;b--){const k=u.lods[b];if(!(h&&k.max_height>=2048)&&!(we()&&k.max_height>4096)&&(k.max_height>D||!L&&b===0)){if(L=!0,o.texture_lod=b,B&&o.texture_lod<r.lastLodLevel_Texture){const O=k.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${O}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 Bt{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class ue{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const B=j("debugprogressive"),Gt=B==="colors",qt=j("noprogressive"),Te=Symbol("Needle:LODSManager"),Se=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),ze=tt,S={mesh_lod:-1,texture_lod:-1},Wt=new Ie,Oe=[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 Nt(){const n=ze.Timer||ze.Clock;return new n}const ke=new ae,F=new ae,Ve=new ae,jt=new C,Ut=new C,Ft=new Ee,N=new C,Q=new C,J=new C,Z=new C;function zt(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return N.set(s,o,e.z).applyMatrix4(t).z<0}function Xe(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:l=-1,xrEnabled:a=!1,debugDrawLine:c,warnMissingPrimitiveDensities:d=!1}=n,u=f.getMeshLODExtension(t)?.lods,p=f.getPrimitiveIndex(t),m=n.target??{level:l,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new C,centrality:1};m.level=l,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1;let M=n.boundingBox??t.boundingBox;if(M||(t.computeBoundingBox(),M=t.boundingBox),!M)return m;if(ke.copy(M).applyMatrix4(e),r.isPerspectiveCamera&&zt(ke,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(F.copy(ke).applyMatrix4(s),a&&r.isPerspectiveCamera&&r.fov>70){const L=F.min,b=F.max;let R=L.x,O=L.y,K=b.x,I=b.y;const w=2,x=1.5,y=(L.x+b.x)*.5,P=(L.y+b.y)*.5;R=(R-y)*w+y,O=(O-P)*w+P,K=(K-y)*w+y,I=(I-P)*w+P;const ne=R<0&&K>0?0:Math.min(Math.abs(L.x),Math.abs(b.x)),ie=O<0&&I>0?0:Math.min(Math.abs(L.y),Math.abs(b.y)),de=Math.max(ne,ie);m.centrality=(x-de)*(x-de)*(x-de)}const h=F.getSize(jt);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),Ve.copy(M).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const v=Ve.getSize(Ut),_=Math.max(h.x,h.y),T=Math.max(v.x,v.y);_!==0&&T!==0&&(h.z=v.z/T*_);const D=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=D,m.screenspaceVolume.copy(h),D<=0)return m;if(c){const L=Ft.copy(s);L.invert(),N.copy(F.min),Q.copy(F.max),Q.x=N.x,J.copy(F.max),J.y=N.y,Z.copy(F.max);const b=(N.z+Z.z)*.5;N.z=Q.z=J.z=Z.z=b,N.applyMatrix4(L),Q.applyMatrix4(L),J.applyMatrix4(L),Z.applyMatrix4(L),c(N,Q,255),c(N,J,255),c(Q,Z,255),c(J,Z,255)}if(u?.length)for(let L=0;L<u.length;L++){const b=u[L],R=b.densities?.[p]||b.density||1e-5;if(p>0&&d&&Ne()&&!b.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.")),R/D<o){m.level=L;break}}return m}class A{static debugDrawLine;static getObjectLODState(t){return t[Se]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static getPlugins(){return W}static get(t,e){if(t[Te])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Te];const r=new A(t,{engine:"unknown",...e});return t[Te]=r,r}renderer;context;projectionScreenMatrix=new Ee;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new ue(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),Ne()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return ue.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#s=0;#o=0;#i=0;#r=0;#n=Nt();_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(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n.update?.(),e.#o=Math.max(e.#n.getDelta(),1/1e3),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.getRenderList(t,e,r);if(!s)return;const o=s.opaque;let i=!0;if(o.length===1){const l=o[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(qt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.getRenderList(t,e,0);if(!r)return;const s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const a of s){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){B&&(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 c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}const i=r.transparent;for(const a of i){const c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}const l=r.transmissive;for(const a of l){const c=a.object;(c instanceof V||c.isMesh)&&this.updateLODs(t,e,c,o)}}getRenderList(t,e,r){const s=this.renderer;let o=null;if(s.isWebGPURenderer===!0){const i=s._renderLists;if(!i)return null;o=i.get(t,e)}else if(s.isWebGLRenderer===!0){const i=s.renderLists;if(!i)return null;o=i.get(t,r)}return o?{opaque:o.opaque||[],transparent:o.transparent||[],transmissive:o.transmissive||o.transparentDoublePass||[],transparentDoublePass:o.transparentDoublePass||[]}:null}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[Se];if(o||(o=new Vt,r[Se]=o),o.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:H;i>=0?(S.mesh_lod=i,S.texture_lod=i):(this.calculateLodLevel(e,r,o,s,S),S.mesh_lod=Math.round(S.mesh_lod),S.texture_lod=Math.round(S.texture_lod)),S.mesh_lod>=0&&this.loadProgressiveMeshes(r,S.mesh_lod),r.material&&S.texture_lod>=0&&this.loadProgressiveTextures(r.material,S.texture_lod,i),g&&r.material&&!r.isGizmo&&Fe(r.material),Gt&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&He(r.material,S.mesh_lod);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,S);o.lastLodLevel_Mesh=S.mesh_lod,o.lastLodLevel_Texture=S.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[U]===void 0||e<t[U])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[U]!=r,e=r),s){t[U]=e;const i=o?{force:!0}:void 0,l=f.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(c=>c({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,l,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[U]!=s,e=s),r){t[U]=e;const o=t.geometry,i=f.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ue.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new Ae;_tempWorldPosition=new C;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=f.getMeshLODExtension(e.geometry)?.lods,c=f.getPrimitiveIndex(e.geometry),d=a&&a.length>0,u=f.getMaterialMinMaxLODsCount(e.material),p=u.min_count!==1/0&&u.min_count>=0&&u.max_count>=0;if(!d&&!p){o.mesh_lod=0,o.texture_lod=0;return}d||(l=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[A.$skinnedMeshBoundsOffset]){const _=A.skinnedMeshBoundsFrameOffsetCounter++;h[A.$skinnedMeshBoundsOffset]=_}const v=h[A.$skinnedMeshBoundsOffset];if((r.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const _=Y(h),T=h.geometry;_&&(h.geometry=_),h.computeBoundingBox(),h.geometry=T}}M=h.boundingBox}if(M){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const v=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(v)){o.mesh_lod=0,o.texture_lod=0;return}}const h=Xe({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:M,xrEnabled:this.renderer.xr.enabled,debugDrawLine:B?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,l=!0)}if(l?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,B&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=a?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[c].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=u.max_count-1,B){const v=u.lods[u.max_count-1];B&&console.log(`First Texture LOD ${o.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let _=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(_*=1.5);const T=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,D=m/T*_;let L=!1;for(let b=u.lods.length-1;b>=0;b--){const R=u.lods[b];if(!(h&&R.max_height>=2048)&&!(be()&&R.max_height>4096)&&(R.max_height>D||!L&&b===0)){if(L=!0,o.texture_lod=b,B&&o.texture_lod<r.lastLodLevel_Texture){const O=R.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${O}px
|
|
8
8
|
Screensize: ${D.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${v.toFixed(1)}
|
|
9
|
-
${e.name}`)}break}}}}else o.texture_lod=0}}class
|
|
10
|
-
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(c=>c.toString()=="Symbol(renderer)"),l=o.find(c=>c.toString()=="Symbol(scene)"),a=o.find(c=>c.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&l!=null&&(e=n[l]),!r&&a!=null&&(r=n[a])}if(t&&e){let s=function(){if(r){let i=0;const l=setInterval(()=>{if(i++>5){clearInterval(l);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=A.get(t,{engine:"model-viewer"});return A.addPlugin(new
|
|
9
|
+
${e.name}`)}break}}}}else o.texture_lod=0}}class Vt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new C;lastCentrality=0}function He(n,t){if(!(t<0)){if(Array.isArray(n)){for(const e of n)He(e,t);return}"color"in n&&n.color instanceof Ie&&(n.color.copy(Ke(t,Wt)),n.needsUpdate=!0)}}function Ke(n,t){const e=Math.max(0,Math.min(Oe.length-1,Math.floor(n)));return t.setHex(Oe[e])}const Ye=Symbol("NEEDLE_mesh_lod"),ce=Symbol("NEEDLE_texture_lod");let Re=null;function Pe(){const n=Xt();n&&(n.mapURLs(function(t){return Qe(),t}),Qe(),Re?.disconnect(),Re=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Ze(r)})})}),Re.observe(document,{childList:!0,subtree:!0}))}function Xt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Pe()}),null)}function Qe(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(n=>{Ze(n)})}const Je=new WeakSet;let Ht=0;function Ze(n){if(!n||Je.has(n))return null;Je.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++Ht+`
|
|
10
|
+
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(c=>c.toString()=="Symbol(renderer)"),l=o.find(c=>c.toString()=="Symbol(scene)"),a=o.find(c=>c.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&l!=null&&(e=n[l]),!r&&a!=null&&(r=n[a])}if(t&&e){let s=function(){if(r){let i=0;const l=setInterval(()=>{if(i++>5){clearInterval(l);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=A.get(t,{engine:"model-viewer"});return A.addPlugin(new Kt),o.enable(),o.addEventListener("changed",()=>{r?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(n)}),n.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class Kt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,s){this.tryParseMeshLOD(e,s),this.tryParseTextureLOD(e,s)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[ce]==!0)return;e[ce]=!0;const r=this.tryGetCurrentGLTF(t),s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(o&&r&&e.material){let i=function(a){if(a[ce]==!0)return;a[ce]=!0,a.userData&&(a.userData.LOD=-1);const c=Object.keys(a);for(let d=0;d<c.length;d++){const u=c[d],p=a[u];if(p?.isTexture===!0){const m=p.userData?.associations?.textures;if(m==null)continue;const M=r.parser.json.textures[m];if(!M){console.warn("Texture data not found for texture index "+m);continue}if(M?.extensions?.[q]){const h=M.extensions[q];h&&o&&f.registerTexture(o,p,h.lods.length,m,h)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)i(a);else i(l)}}tryParseMeshLOD(t,e){if(e[Ye]==!0)return;e[Ye]=!0;const r=this.tryGetCurrentModelViewer(t),s=this.getUrl(r);if(!s)return;const o=e.userData?.gltfExtensions?.[q];if(o&&s){const i=e.uuid;f.registerMesh(s,i,e,0,o.lods.length,o)}}}function et(...n){let t,e,r,s;switch(n.length){case 2:[r,e]=n,s={};break;case 3:[r,e,s]=n;break;case 4:[t,e,r,s]=n;break;default:throw new Error("Invalid arguments")}le(e),ge(r),ve(r,{progressive:!0,...s?.hints}),r.register(i=>new f(i));const o=A.get(e);return s?.enableLODsManager!==!1&&o.enable(),o}if(Pe(),!Rt){const n={gltfProgressive:{useNeedleProgressive:et,LODsManager:A,configureLoader:ve,getRaycastMesh:Y,useRaycastMeshes:Ue}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}export{q as EXTENSION_NAME,A as LODsManager,f as NEEDLE_progressive,$e as VERSION,ge as addDracoAndKTX2Loaders,Xe as calculateMeshLODLevel,ve as configureLoader,le as createLoaders,Lt as disposeLoaders,Ke as getLODColor,Y as getRaycastMesh,Oe as lodDebugColors,Pe as patchModelViewer,je as registerRaycastMesh,Ge as setDracoDecoderLocation,qe as setKTX2TranscoderLocation,et as useNeedleProgressive,Ue as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var He=Object.create;var Se=Object.defineProperty;var Ye=Object.getOwnPropertyDescriptor;var Qe=Object.getOwnPropertyNames;var Je=Object.getPrototypeOf,Ze=Object.prototype.hasOwnProperty;var et=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Qe(t))!Ze.call(o,r)&&r!==e&&Se(o,r,{get:()=>t[r],enumerable:!(s=Ye(t,r))||s.enumerable});return o};var tt=(o,t,e)=>(e=o!=null?He(Je(o)):{},et(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"),st=require("three/examples/jsm/libs/meshopt_decoder.module.js"),rt=require("three/examples/jsm/loaders/DRACOLoader.js"),nt=require("three/examples/jsm/loaders/KTX2Loader.js");var le=typeof document<"u"?document.currentScript:null;function ot(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 it=ot(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 at=B,ut=K,$e=new URL(B+"draco_decoder.js");$e.searchParams.append("range","true");fetch($e,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${B} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),B===at&&Be("./include/draco/"),K===ut&&qe("./include/ktx2/")}).finally(()=>{Fe()});const lt=()=>({dracoDecoderPath:B,ktx2TranscoderPath:K});function Be(o){B=o,P&&P[me]!=B?(console.debug("Updating Draco decoder path to "+o),P[me]=B,P.setDecoderPath(B),P.preload()):console.debug("Setting Draco decoder path to "+o)}function qe(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 Fe(),o?q.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:q,meshoptDecoder:ae}}function Me(o){o.dracoLoader||o.setDRACOLoader(P),o.ktx2Loader||o.setKTX2Loader(q),o.meshoptDecoder||o.setMeshoptDecoder(ae)}const me=Symbol("dracoDecoderPath");let P,ae,q;function Fe(){P||(P=new rt.DRACOLoader,P[me]=B,P.setDecoderPath(B),P.setDecoderConfig({type:"js"}),P.preload()),q||(q=new nt.KTX2Loader,q.setTranscoderPath(K),q.init()),ae||(ae=st.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 ct=De.GLTFLoader.prototype.load;function ft(...o){const t=ye.get(this);let e=o[0];const s=dt(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,ct?.call(this,...o)}De.GLTFLoader.prototype.load=ft;function dt(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 ht(o){const t=o.image;return t!=null&&typeof t=="object"?t:null}function Le(o){const t=ht(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 gt(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 We(){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 pt{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 mt(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=yt(o);return e*s*r*i*(1-Math.pow(.25,n))/(1-.25)}function yt(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 xt=typeof window>"u"&&typeof document>"u",we=Symbol("needle:raycast-mesh");function Z(o){return o?.[we]instanceof f.BufferGeometry?o[we]:null}function Ue(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!Z(o)){const s=Lt(t);s.userData={isRaycastMesh:!0},o[we]=s}}function Ee(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 Lt(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 Ve(o){if(p&&ne!==void 0)if(Array.isArray(o))for(const t of o)Ve(t);else o&&"wireframe"in o&&(o.wireframe=ne===!0)}const se=new Array;let wt=0;const vt=Te()?2:10;function Dt(o){if(se.length<vt){const s=se.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const r=be.createWorker(o||{});return se.push(r),r}const t=wt++%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){Mt(r.result),n.resolve(r.result);const i=n.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href: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=lt();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 Mt(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 _t=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)&&Ue(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=`~${(
|
|
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[
|
|
4
|
-
`,b),x(null)));if(!T)return x(null);const E=T.parser;n&&console.log("Loading finished "+c,v.guid);let A=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}}A++}if(b){let y=await E.getDependency("texture",A);return y&&m.assignLODInformation(i.url,y,u,e,void 0),n&&console.log('change "'+t.name+'" → "'+y.name+'"',c,A,y,
|
|
5
|
-
`,l),null));if(this.disposedRequests.has(t))return{found:!0,value:null};let a=!1;return u==null||(u instanceof
|
|
1
|
+
"use strict";var Ye=Object.create;var Ce=Object.defineProperty;var Qe=Object.getOwnPropertyDescriptor;var Je=Object.getOwnPropertyNames;var Ze=Object.getPrototypeOf,et=Object.prototype.hasOwnProperty;var tt=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Je(t))!et.call(o,r)&&r!==e&&Ce(o,r,{get:()=>t[r],enumerable:!(s=Qe(t,r))||s.enumerable});return o};var st=(o,t,e)=>(e=o!=null?Ye(Ze(o)):{},tt(t||!o||!o.__esModule?Ce(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("three"),De=require("three/examples/jsm/loaders/GLTFLoader.js"),rt=require("three/examples/jsm/libs/meshopt_decoder.module.js"),nt=require("three/examples/jsm/loaders/DRACOLoader.js"),ot=require("three/examples/jsm/loaders/KTX2Loader.js");var ue=typeof document<"u"?document.currentScript:null;function it(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 at=it(d),Be="";globalThis.GLTF_PROGRESSIVE_VERSION=Be;console.debug("[gltf-progressive] version -");let q="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",K="https://cdn.needle.tools/static/three/0.179.1/basis2/";const ut=q,lt=K,qe=new URL(q+"draco_decoder.js");qe.searchParams.append("range","true");fetch(qe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${q} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),q===ut&&Fe("./include/draco/"),K===lt&&We("./include/ktx2/")}).finally(()=>{_e&&Te()});const ct=()=>({dracoDecoderPath:q,ktx2TranscoderPath:K});function Fe(o){q=o,P&&P[me]!=q?(console.debug("Updating Draco decoder path to "+o),P[me]=q,P.setDecoderPath(q),P.preload()):console.debug("Setting Draco decoder path to "+o)}function We(o){K=o,B&&B.transcoderPath!=K?(console.debug("Updating KTX2 transcoder path to "+o),B.setTranscoderPath(K),B.init()):console.debug("Setting KTX2 transcoder path to "+o)}function ae(o){_e=!0;const t=Te();return o?t.ktx2Loader.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),t}function dt(){_e=!1,P?.dispose(),B?.dispose(),P=void 0,B=void 0}function Me(o){const{dracoLoader:t,ktx2Loader:e,meshoptDecoder:s}=Te();o.dracoLoader||o.setDRACOLoader(t),o.ktx2Loader||o.setKTX2Loader(e),o.meshoptDecoder||o.setMeshoptDecoder(s)}const me=Symbol("dracoDecoderPath");let _e=!0,P,le,B;function Te(){return P||(P=new nt.DRACOLoader,P[me]=q,P.setDecoderPath(q),P.setDecoderConfig({type:"js"}),P.preload()),B||(B=new ot.KTX2Loader,B.setTranscoderPath(K),B.init()),le||(le=rt.MeshoptDecoder),{dracoLoader:P,ktx2Loader:B,meshoptDecoder:le}}const ye=new WeakMap;function be(o,t){let e=ye.get(o);e?e=Object.assign(e,t):e=t,ye.set(o,e)}const ft=De.GLTFLoader.prototype.load;function ht(...o){const t=ye.get(this);let e=o[0];const s=gt(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,ft?.call(this,...o)}De.GLTFLoader.prototype.load=ht;function gt(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 Pe(o){return o!=null&&o.data!=null}function xe(o){const t=o.source?.data;return t!=null&&typeof t=="object"?t:null}function pt(o){const t=o.image;return t!=null&&typeof t=="object"?t:null}function Le(o){const t=pt(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 mt(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 Oe(){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 Ue(){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 yt{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 xt(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=Lt(o);return e*s*r*i*(1-Math.pow(.25,n))/(1-.25)}function Lt(o){let t=4;const e=o.format;e===d.RedFormat||e===d.RedIntegerFormat?t=1:e===d.RGFormat||e===d.RGIntegerFormat?t=2:e===d.RGBFormat||e===1029?t=3:(e===d.RGBAFormat||e===d.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 wt=typeof window>"u"&&typeof document>"u",we=Symbol("needle:raycast-mesh");function Z(o){return o?.[we]instanceof d.BufferGeometry?o[we]:null}function Ee(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!Z(o)){const s=vt(t);s.userData={isRaycastMesh:!0},o[we]=s}}function Ve(o=!0){if(o){if(te)return;const t=te=d.Mesh.prototype.raycast;d.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;d.Mesh.prototype.raycast=te,te=null}}let te=null;function vt(o){const t=new d.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 ze(o){if(p&&ne!==void 0)if(Array.isArray(o))for(const t of o)ze(t);else o&&"wireframe"in o&&(o.wireframe=ne===!0)}const se=new Array;let Dt=0;const Mt=Oe()?2:10;function _t(o){if(se.length<Mt){const s=se.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const r=Se.createWorker(o||{});return se.push(r),r}const t=Dt++%se.length;return se[t]}class Se{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){Tt(r.result),n.resolve(r.result);const i=n.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ue&&ue.tagName.toUpperCase()==="SCRIPT"&&ue.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new Se(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=ct();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),r=await this._webglRenderer);const u=ae(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 Tt(o){for(const t of o.geometries){const e=t.geometry,s=new d.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 d.Box3,s.boundingBox.min=new d.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new d.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new d.Sphere(new d.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of o.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,{width:n,height:i}=Le(e);s=new d.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 d.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new d.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return o}function ce(o){let t=o;if("isInterleavedBufferAttribute"in o&&o.isInterleavedBufferAttribute){const e=o.data,s=e.array,r=new d.InterleavedBuffer(s,e.stride);t=new d.InterleavedBufferAttribute(r,o.itemSize,s.byteOffset,o.normalized),t.offset=o.offset}else"isBufferAttribute"in o&&o.isBufferAttribute&&(t=new d.BufferAttribute(o.array,o.itemSize,o.normalized),t.usage=o.usage,t.gpuType=o.gpuType,t.updateRanges=o.updateRanges);return t}const bt=N("gltf-progressive-worker");N("gltf-progressive-reduce-mipmaps");const re=N("gltf-progressive-gc"),de=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 f=h.lods[c];f.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,f.height),a.lods[c].max_height=Math.max(a.lods[c].max_height,f.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 d.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(f=>(f&&a.uniforms[l].value!=f&&(a.uniforms[l].value=f,a.uniformsNeedUpdate=!0),f));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],f=u[h];c&&c.isTexture===!0?l.push({material:n,slot:f,texture:c,level:e}):l.push({material:n,slot:f,texture:null,level:e})}return l})}if(t instanceof d.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 f=this.getMaterialTextureSlot(s,r)??t;if(f&&!n){const g=this.getAssignedLODInformation(f);if(g&&g?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",g.level,e,s,f,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[de]=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)&&Ee(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=`~${(xt(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[de]&&(a=h.source[de])}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 f=!1;const g=Array.isArray(a.lods);if(g&&e>=a.lods.length?f=!0:g||(f=!0),f){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=mt(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 f=c+"_"+a.guid,g=await this.tryResolveLODCacheEntry(this.cache.get(f),f,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(f),f,c,t,e,n);if(r!==this.cacheGeneration)return null;if(w.found)return w.value;const L=this.cache.get(f);if(L!==void 0){const x=await this.tryResolveLODCacheEntry(L,f,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(bt){const y=await(await _t({})).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 d.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 k=c;if(v&&Array.isArray(v.lods)){const b=v.lods[e];b.hash&&(k+="?v="+b.hash)}const T=await G.loadAsync(k).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 A=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}}A++}if(b){let y=await E.getDependency("texture",A);return y&&m.assignLODInformation(i.url,y,u,e,void 0),n&&console.log('change "'+t.name+'" → "'+y.name+'"',c,A,y,f),t instanceof d.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(A=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}}A++}if(b){const y=await E.getDependency("mesh",A);if(n&&console.log(`Loaded Mesh "${y.name}"`,c,A,y,f),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(f,_),M.use(_);const O=await _;return r!==this.cacheGeneration||this.cache.get(f)!==_?null:(O!=null?O instanceof d.Texture?(this.cache.set(f,new WeakRef(O)),m._resourceRegistry.register(O,f)):Array.isArray(O)?this.cache.set(f,Promise.resolve(O)):this.cache.set(f,Promise.resolve(O)):this.cache.set(f,Promise.resolve(null)),O)}else if(t instanceof d.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 d.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 d.Texture&&r instanceof d.Texture?Pe(h.image)||xe(h)?h=this.copySettings(r,h):c=!0:h instanceof d.BufferGeometry&&r instanceof d.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 d.Texture&&r instanceof d.Texture?Pe(u.image)||xe(u)?u=this.copySettings(r,u):(a=!0,this.cache.delete(e)):u instanceof d.BufferGeometry&&r instanceof d.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 yt(Oe()?20:50,{debug:p!=!1})}static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const i=new Ot(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 Tt{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"),bt=$==="colors",Ot=N("noprogressive"),de=Symbol("Needle:LODSManager"),he=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),Ce=it,C={mesh_lod:-1,texture_lod:-1},St=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 Rt(){const o=Ce.Timer||Ce.Clock;return new o}const ge=new f.Box3,z=new f.Box3,Pe=new f.Box3,Ct=new f.Vector3,Pt=new f.Vector3,kt=new f.Matrix4,W=new f.Vector3,Y=new f.Vector3,Q=new f.Vector3,J=new f.Vector3;function At(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 ze(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&&At(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,k=x.y,T=S.x,E=S.y;const A=2,b=1.5,y=(x.x+S.x)*.5,D=(x.y+S.y)*.5;G=(G-y)*A+y,k=(k-D)*A+D,T=(T-y)*A+y,E=(E-D)*A+D;const R=G<0&&T>0?0:Math.min(Math.abs(x.x),Math.abs(S.x)),j=k<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(Ct);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(Pt),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=kt.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&&We()&&!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 I{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 I(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),We()&&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=Rt();_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(Ot||(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 It,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&&Ve(s.material),bt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&Ne(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[I.$skinnedMeshBoundsOffset]){const _=I.skinnedMeshBoundsFrameOffsetCounter++;L[I.$skinnedMeshBoundsOffset]=_}const v=L[I.$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=ze({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:$?I.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 k=d.lods.length-1;k>=0;k--){const T=d.lods[k];if(!(L&&T.max_height>=2048)&&!(Te()&&T.max_height>4096)&&(T.max_height>S||!G&&k===0)){if(G=!0,n.texture_lod=k,$&&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 Ot{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"),St=$==="colors",Rt=N("noprogressive"),fe=Symbol("Needle:LODSManager"),he=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),ke=at,C={mesh_lod:-1,texture_lod:-1},Ct=new d.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 Pt(){const o=ke.Timer||ke.Clock;return new o}const ge=new d.Box3,z=new d.Box3,Ae=new d.Box3,kt=new d.Vector3,At=new d.Vector3,It=new d.Matrix4,W=new d.Vector3,Y=new d.Vector3,Q=new d.Vector3,J=new d.Vector3;function Gt(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 Ne(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,f=m.getPrimitiveIndex(t),g=o.target??{level:u,primitiveIndex:f,screenCoverage:0,screenspaceVolume:new d.Vector3,centrality:1};g.level=u,g.primitiveIndex=f,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&&Gt(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,k=x.y,T=S.x,E=S.y;const A=2,b=1.5,y=(x.x+S.x)*.5,D=(x.y+S.y)*.5;G=(G-y)*A+y,k=(k-D)*A+D,T=(T-y)*A+y,E=(E-D)*A+D;const R=G<0&&T>0?0:Math.min(Math.abs(x.x),Math.abs(S.x)),j=k<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(kt);w.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&w.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(w.x*=s.aspect),Ae.copy(M).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Ae.getSize(At),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=It.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?.[f]||S.density||1e-5;if(f>0&&h&&Ue()&&!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 I{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[fe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[fe];const s=new I(t,{engine:"unknown",...e});return t[fe]=s,s}renderer;context;projectionScreenMatrix=new d.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),Ue()&&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=Pt();_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;ae(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(Rt||(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 d.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const i=s.transparent;for(const a of i){const l=a.object;(l instanceof d.Mesh||l.isMesh)&&this.updateLODs(t,e,l,n)}const u=s.transmissive;for(const a of u){const l=a.object;(l instanceof d.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 $t,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&&ze(s.material),St&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&je(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 d.Sphere;_tempWorldPosition=new d.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,f=m.getMaterialMinMaxLODsCount(e.material),g=f.min_count!==1/0&&f.min_count>=0&&f.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[I.$skinnedMeshBoundsOffset]){const _=I.skinnedMeshBoundsFrameOffsetCounter++;L[I.$skinnedMeshBoundsOffset]=_}const v=L[I.$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=Ne({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:$?I.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=f.max_count-1,$){const v=f.lods[f.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 k=f.lods.length-1;k>=0;k--){const T=f.lods[k];if(!(L&&T.max_height>=2048)&&!(Oe()&&T.max_height>4096)&&(T.max_height>S||!G&&k===0)){if(G=!0,n.texture_lod=k,$&&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
|
-
${e.name}`)}break}}}}else n.texture_lod=0}}class
|
|
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=I.get(t,{engine:"model-viewer"});return I.addPlugin(new
|
|
9
|
+
${e.name}`)}break}}}}else n.texture_lod=0}}class $t{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}function je(o,t){if(!(t<0)){if(Array.isArray(o)){for(const e of o)je(e,t);return}"color"in o&&o.color instanceof d.Color&&(o.color.copy(Xe(t,Ct)),o.needsUpdate=!0)}}function Xe(o,t){const e=Math.max(0,Math.min(ve.length-1,Math.floor(o)));return t.setHex(ve[e])}const Ie=Symbol("NEEDLE_mesh_lod"),ie=Symbol("NEEDLE_texture_lod");let pe=null;function Re(){const o=Bt();o&&(o.mapURLs(function(t){return Ge(),t}),Ge(),pe?.disconnect(),pe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&Ke(s)})})}),pe.observe(document,{childList:!0,subtree:!0}))}function Bt(){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"),Re()}),null)}function Ge(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Ke(t)})}const $e=new WeakSet;let qt=0;function Ke(o){if(!o||$e.has(o))return null;$e.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++qt+`
|
|
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=I.get(t,{engine:"model-viewer"});return I.addPlugin(new Ft),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 Ft{_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],f=a[c];if(f?.isTexture===!0){const g=f.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,f,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[Ie]==!0)return;e[Ie]=!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 He(...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")}ae(e),Me(s),be(s,{progressive:!0,...r?.hints}),s.register(i=>new m(i));const n=I.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}Re();if(!wt){const o={gltfProgressive:{useNeedleProgressive:He,LODsManager:I,configureLoader:be,getRaycastMesh:Z,useRaycastMeshes:Ve}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=F;exports.LODsManager=I;exports.NEEDLE_progressive=m;exports.VERSION=Be;exports.addDracoAndKTX2Loaders=Me;exports.calculateMeshLODLevel=Ne;exports.configureLoader=be;exports.createLoaders=ae;exports.disposeLoaders=dt;exports.getLODColor=Xe;exports.getRaycastMesh=Z;exports.lodDebugColors=ve;exports.patchModelViewer=Re;exports.registerRaycastMesh=Ee;exports.setDracoDecoderLocation=Fe;exports.setKTX2TranscoderLocation=We;exports.useNeedleProgressive=He;exports.useRaycastMeshes=Ve;
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { version as VERSION } from "./version.js";
|
|
|
2
2
|
export * from "./extension.js";
|
|
3
3
|
export * from "./plugins/index.js";
|
|
4
4
|
export { LODsManager, calculateMeshLODLevel, getLODColor, lodDebugColors, type LOD_Results, type MeshLODSelectionOptions, type MeshLODSelectionResult } from "./lods.manager.js";
|
|
5
|
-
export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders, configureLoader } from "./loaders.js";
|
|
5
|
+
export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, disposeLoaders, addDracoAndKTX2Loaders, configureLoader } from "./loaders.js";
|
|
6
6
|
export { getRaycastMesh, registerRaycastMesh, useRaycastMeshes } from "./utils.js";
|
|
7
7
|
import { WebGLRenderer } from "three";
|
|
8
8
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { version as VERSION } from "./version.js";
|
|
|
2
2
|
export * from "./extension.js";
|
|
3
3
|
export * from "./plugins/index.js";
|
|
4
4
|
export { LODsManager, calculateMeshLODLevel, getLODColor, lodDebugColors } from "./lods.manager.js";
|
|
5
|
-
export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, addDracoAndKTX2Loaders, configureLoader } from "./loaders.js";
|
|
5
|
+
export { setDracoDecoderLocation, setKTX2TranscoderLocation, createLoaders, disposeLoaders, addDracoAndKTX2Loaders, configureLoader } from "./loaders.js";
|
|
6
6
|
export { getRaycastMesh, registerRaycastMesh, useRaycastMeshes } from "./utils.js";
|
|
7
7
|
import { addDracoAndKTX2Loaders, configureLoader, createLoaders } from "./loaders.js";
|
|
8
8
|
import { NEEDLE_progressive } from "./extension.js";
|
package/lib/loaders.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export declare function createLoaders(renderer: WebGLRenderer | null): {
|
|
|
37
37
|
decodeGltfBuffer: (target: Uint8Array, count: number, size: number, source: Uint8Array, mode: string, filter?: string) => void;
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
|
+
/** Release the shared Draco and KTX2 workers when no engine context uses them. */
|
|
41
|
+
export declare function disposeLoaders(): void;
|
|
40
42
|
export declare function addDracoAndKTX2Loaders(loader: GLTFLoader): void;
|
|
41
43
|
/**
|
|
42
44
|
* Smart loading hints can be used by needle infrastructure to deliver assets optimized for a specific usecase.
|