@needle-tools/gltf-progressive 3.4.0-next.a30adb3 → 3.4.0-next.dbcee85
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/gltf-progressive.js +338 -337
- package/gltf-progressive.min.js +8 -7
- package/gltf-progressive.umd.cjs +8 -7
- package/lib/extension.d.ts +1 -1
- package/lib/extension.js +26 -23
- package/package.json +1 -1
package/gltf-progressive.min.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import{BufferGeometry as W,Mesh as F,Box3 as ie,Vector3 as C,Sphere as Le,CompressedTexture as Ne,Texture as $,Matrix3 as je,InterleavedBuffer as We,InterleavedBufferAttribute as Fe,BufferAttribute as Ue,TextureLoader as ze,Matrix4 as ve,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ne}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as Ye}from"three/examples/jsm/loaders/KTX2Loader.js";const _e="";globalThis.GLTF_PROGRESSIVE_VERSION=_e,console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",U="https://cdn.needle.tools/static/three/0.179.1/basis2/";const He=I,Qe=U,be=new URL(I+"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 ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===He&&De("./include/draco/"),U===Qe&&Me("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:I,ktx2TranscoderPath:U});function De(n){I=n,P&&P[le]!=I?(console.debug("Updating Draco decoder path to "+n),P[le]=I,P.setDecoderPath(I),P.preload()):console.debug("Setting Draco decoder path to "+n)}function Me(n){U=n,E&&E.transcoderPath!=U?(console.debug("Updating KTX2 transcoder path to "+n),E.setTranscoderPath(U),E.init()):console.debug("Setting KTX2 transcoder path to "+n)}function Z(n){return Oe(),n?E.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:E,meshoptDecoder:ee}}function ae(n){n.dracoLoader||n.setDRACOLoader(P),n.ktx2Loader||n.setKTX2Loader(E),n.meshoptDecoder||n.setMeshoptDecoder(ee)}const le=Symbol("dracoDecoderPath");let P,ee,E;function Oe(){P||(P=new Ke,P[le]=I,P.setDecoderPath(I),P.setDecoderConfig({type:"js"}),P.preload()),E||(E=new Ye,E.setTranscoderPath(U),E.init()),ee||(ee=Xe)}const ue=new WeakMap;function de(n,t){let e=ue.get(n);e?e=Object.assign(e,t):e=t,ue.set(n,e)}const Ze=ne.prototype.load;function et(...n){const t=ue.get(this);let e=n[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return n[0]=e,Ze?.call(this,...n)}ne.prototype.load=et,z("debugprogressive");function z(n){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(n);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function 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 s=n.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Se(){return te!==void 0||(te=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",te)),te}let te;function Pe(){if(typeof window>"u")return!1;const n=new URL(window.location.href),t=n.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(n.hostname);return n.hostname==="127.0.0.1"||t}class st{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}const rt=typeof window>"u"&&typeof document>"u",ce=Symbol("needle:raycast-mesh");function V(n){return n?.[ce]instanceof W?n[ce]:null}function Te(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!V(n)){const e=ot(t);e.userData={isRaycastMesh:!0},n[ce]=e}}function Ae(n=!0){if(n){if(X)return;const t=X=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let i;r&&o.isMesh&&(i=o.geometry,o.geometry=r),t.call(this,e,s),i&&(o.geometry=i)}}else{if(!X)return;F.prototype.raycast=X,X=null}}let X=null;function ot(n){const t=new W;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const N=new Array,g=z("debugprogressive");let K,q=-1;if(g){let n=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(K=!K,console.log(`Toggle wireframe [${K}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function Ce(n){if(g&&K!==void 0)if(Array.isArray(n))for(const t of n)Ce(t);else n&&"wireframe"in n&&(n.wireframe=K===!0)}const Y=new Array;let it=0;const nt=Se()?2:10;function at(n){if(Y.length<nt){const e=Y.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const s=we.createWorker(n||{});return Y.push(s),s}const t=it++%Y.length;return Y[t]}class we{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){lt(o.result),r.resolve(o.result);const i=r.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/loader.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new we(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=Z(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const i={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};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 lt(n){for(const t of n.geometries){const e=t.geometry,s=new W;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(he(o))}for(const o in e.attributes){const r=e.attributes[o],i=he(r);s.setAttribute(o,i)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(i=>he(i));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ie,s.boundingBox.min=new C(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new C(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new Le(new C(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of n.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;s=new Ne(o,r,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new $(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 je(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return n}function he(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,s=e.array,o=new We(s,e.stride);t=new Fe(o,n.itemSize,s.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new Ue(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const ut=z("gltf-progressive-worker"),dt=z("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class p{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=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 u=l.uniforms[a].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function i(l,a){const u=s.getAssignedLODInformation(l);if(u){const x=s.lodInfos.get(u.key);if(x&&x.lods){a.min_count=Math.min(a.min_count,x.lods.length),a.max_count=Math.max(a.max_count,x.lods.length);for(let y=0;y<x.lods.length;y++){const w=x.lods[y];w.width&&(a.lods[y]=a.lods[y]||{min_height:1/0,max_height:0},a.lods[y].min_height=Math.min(a.lods[y].min_height,w.height),a.lods[y].max_height=Math.max(a.lods[y].max_height,w.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const i=t[r];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of N)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,p.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const i=o.index||0;r=r[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:g&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const i=this.assignTextureLOD(r,e);o.push(i)}return Promise.all(o).then(r=>{const i=new Array;for(const l of r)Array.isArray(l)&&i.push(...l);return i})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const i=s;for(const l of Object.keys(i.uniforms)){const a=i.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(x=>(x&&i.uniforms[l].value!=x&&(i.uniforms[l].value=x,i.uniformsNeedUpdate=!0),x));o.push(u),r.push(l)}}}else for(const i of Object.keys(s)){const l=s[i];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,i);o.push(a),r.push(i)}}return Promise.all(o).then(i=>{const l=new Array;for(let a=0;a<i.length;a++){const u=i[a],x=r[a];u&&u.isTexture===!0?l.push({material:s,slot:x,texture:u,level:e}):l.push({material:s,slot:x,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):p.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const i=s[o];if(i&&!g){const l=this.getAssignedLODInformation(i);if(l&&l?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,i,r),null}if(dt&&r.mipmaps){const l=r.mipmaps.length;r.mipmaps.length=Math.min(r.mipmaps.length,3),l!==r.mipmaps.length&&g&&console.debug(`Reduced mipmap count from ${l} to ${r.mipmaps.length} for ${r.uuid}: ${r.image?.width}x${r.image?.height}.`)}s[o]=r}return r}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&p.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o){if(!o.lods){g&&console.warn("Texture has no LODs",o);return}let r=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===s&&(r=!0,p.registerTexture(this.url,i,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(i=>{i&&p.registerTexture(this.url,i,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[B];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const i=this.parser.associations.get(r);i?.meshes===s&&p.registerMesh(this.url,o.guid,r,o.lods.length,i.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[ge]=r);const i=r.guid;p.assignLODInformation(t,e,i,s,o),p.lodInfos.set(i,r),p.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,o,r,i)=>{const l=s.geometry;if(!l){g&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},i,s),p.assignLODInformation(t,l,e,o,r),p.lodInfos.set(e,i);let a=p.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],p.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Te(s,l);for(const u of N)u.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)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.previouslyLoaded)s.includes(t)&&(this._disposeCacheEntry(o),this.previouslyLoaded.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.previouslyLoaded)this._disposeCacheEntry(e);this.previouslyLoaded.clear()}}static _disposeCacheEntry(t){t instanceof WeakRef?t.deref()?.dispose():t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.dispose()}).catch(()=>{})}static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=p.previouslyLoaded.get(t);console.debug(`[gltf-progressive] FinalizationRegistry cleanup: Resource GC'd for ${t}.`),e instanceof WeakRef&&(e.deref()||(p.previouslyLoaded.delete(t),g&&console.log(`[gltf-progressive] Cache entry auto-cleaned (GC'd): ${t}`)))});static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let i;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(i=l.source[ge])}if(i||(i=p.lodInfos.get(r)),!i)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,p.lodInfos);else{if(e>0){let u=!1;const x=Array.isArray(i.lods);if(x&&e>=i.lods.length?u=!0:x||(u=!0),u){const y=this.lowresCache.get(r);if(y){const w=y.deref();if(w)return w;this.lowresCache.delete(r),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(i.lods)?i.lods[e]?.path:i.lods;if(!l)return g&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const a=tt(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const u=a+"_"+i.guid,x=await this.queue.slot(a),y=this.previouslyLoaded.get(u);if(y!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),y instanceof WeakRef){const d=y.deref();if(d){let m=d,v=!1;if(m instanceof $&&t instanceof $?m.image?.data||m.source?.data?m=this.copySettings(t,m):v=!0:m instanceof W&&t instanceof W&&(m.attributes.position?.array||(v=!0)),!v)return m}this.previouslyLoaded.delete(u),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let d=await y.catch(v=>(console.error(`Error loading LOD ${e} from ${a}
|
|
2
|
-
|
|
3
|
-
`,
|
|
1
|
+
import{BufferGeometry as W,Mesh as F,Box3 as ne,Vector3 as C,Sphere as Le,CompressedTexture as Ne,Texture as R,Matrix3 as je,InterleavedBuffer as We,InterleavedBufferAttribute as Fe,BufferAttribute as Ue,TextureLoader as ze,Matrix4 as ve,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ie}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as Ye}from"three/examples/jsm/loaders/KTX2Loader.js";const _e="";globalThis.GLTF_PROGRESSIVE_VERSION=_e,console.debug("[gltf-progressive] version -");let I="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",U="https://cdn.needle.tools/static/three/0.179.1/basis2/";const He=I,Qe=U,be=new URL(I+"draco_decoder.js");be.searchParams.append("range","true"),fetch(be,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${I} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),I===He&&De("./include/draco/"),U===Qe&&Me("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:I,ktx2TranscoderPath:U});function De(i){I=i,P&&P[le]!=I?(console.debug("Updating Draco decoder path to "+i),P[le]=I,P.setDecoderPath(I),P.preload()):console.debug("Setting Draco decoder path to "+i)}function Me(i){U=i,E&&E.transcoderPath!=U?(console.debug("Updating KTX2 transcoder path to "+i),E.setTranscoderPath(U),E.init()):console.debug("Setting KTX2 transcoder path to "+i)}function Z(i){return Oe(),i?E.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:E,meshoptDecoder:ee}}function ae(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(E),i.meshoptDecoder||i.setMeshoptDecoder(ee)}const le=Symbol("dracoDecoderPath");let P,ee,E;function Oe(){P||(P=new Ke,P[le]=I,P.setDecoderPath(I),P.setDecoderConfig({type:"js"}),P.preload()),E||(E=new Ye,E.setTranscoderPath(U),E.init()),ee||(ee=Xe)}const ue=new WeakMap;function ce(i,t){let e=ue.get(i);e?e=Object.assign(e,t):e=t,ue.set(i,e)}const Ze=ie.prototype.load;function et(...i){const t=ue.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,r=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${r}`:this.requestHeader.Accept=`*/*;usecase=${r}`,e=s.toString()}return i[0]=e,Ze?.call(this,...i)}ie.prototype.load=et,z("debugprogressive");function z(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function tt(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Se(){return te!==void 0||(te=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",te)),te}let te;function Pe(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class st{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}const rt=typeof window>"u"&&typeof document>"u",de=Symbol("needle:raycast-mesh");function V(i){return i?.[de]instanceof W?i[de]:null}function Te(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[de]=e}}function Ae(i=!0){if(i){if(X)return;const t=X=F.prototype.raycast;F.prototype.raycast=function(e,s){const o=this,r=V(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!X)return;F.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new W;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const N=new Array,g=z("debugprogressive");let K,q=-1;if(g){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(K=!K,console.log(`Toggle wireframe [${K}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(q=s,console.log(`Set LOD level to [${q}]`))})}function Ce(i){if(g&&K!==void 0)if(Array.isArray(i))for(const t of i)Ce(t);else i&&"wireframe"in i&&(i.wireframe=K===!0)}const Y=new Array;let nt=0;const it=Se()?2:10;function at(i){if(Y.length<it){const e=Y.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const s=we.createWorker(i||{});return Y.push(s),s}const t=nt++%Y.length;return Y[t]}class we{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){lt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},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/loader.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new we(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const r=Z(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const n={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:r};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function lt(i){for(const t of i.geometries){const e=t.geometry,s=new W;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(he(o))}for(const o in e.attributes){const r=e.attributes[o],n=he(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const r=e.morphAttributes[o].map(n=>he(n));s.morphAttributes[o]=r}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new ne,s.boundingBox.min=new C(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new C(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new Le(new C(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new Ne(o,r,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new R(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 je(...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 i}function he(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new We(s,e.stride);t=new Fe(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new Ue(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=z("gltf-progressive-worker"),ct=z("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class m{get name(){return $}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=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 u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const y=s.lodInfos.get(u.key);if(y&&y.lods){a.min_count=Math.min(a.min_count,y.lods.length),a.max_count=Math.max(a.max_count,y.lods.length);for(let x=0;x<y.lods.length;x++){const w=y.lods[x];w.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,w.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,w.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of N)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:g&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(y=>(y&&n.uniforms[l].value!=y&&(n.uniforms[l].value=y,n.uniformsNeedUpdate=!0),y));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],y=r[a];u&&u.isTexture===!0?l.push({material:s,slot:y,texture:u,level:e}):l.push({material:s,slot:y,texture:null,level:e})}return l})}if(t instanceof R||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!g){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),null}if(ct&&r.mipmaps){const l=r.mipmaps.length;r.mipmaps.length=Math.min(r.mipmaps.length,3),l!==r.mipmaps.length&&g&&console.debug(`Reduced mipmap count from ${l} to ${r.mipmaps.length} for ${r.uuid}: ${r.image?.width}x${r.image?.height}.`)}s[o]=r}return r}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[$];if(o){if(!o.lods){g&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,m.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&m.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&m.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[ge]=r);const n=r.guid;m.assignLODInformation(t,e,n,s,o),m.lodInfos.set(n,r),m.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){g&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),m.assignLODInformation(t,l,e,o,r),m.lodInfos.set(e,n);let a=m.lowresCache.get(e)?.deref();a?a.push(s.geometry):a=[s.geometry],m.lowresCache.set(e,new WeakRef(a)),o>0&&!V(s)&&Te(s,l);for(const u of N)u.onRegisteredNewMesh?.(s,n)};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)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear()}}static _disposeCacheEntry(t){t instanceof WeakRef?t.deref()?.dispose():t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);g&&console.debug(`[gltf-progressive] Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),g&&console.log(`[gltf-progressive] Cache entry auto-cleaned (GC'd): ${t}`)))});static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(n=l.source[ge])}if(n||(n=m.lodInfos.get(r)),!n)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let u=!1;const y=Array.isArray(n.lods);if(y&&e>=n.lods.length?u=!0:y||(u=!0),u){const x=this.lowresCache.get(r);if(x){const w=x.deref();if(w)return w;this.lowresCache.delete(r),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return g&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=tt(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,y=await this.queue.slot(a),x=this.cache.get(u);if(x!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${u}`),x instanceof WeakRef){const c=x.deref();if(c){let p=c,v=!1;if(p instanceof R&&t instanceof R?p.image?.data||p.source?.data?p=this.copySettings(t,p):v=!0:p instanceof W&&t instanceof W&&(p.attributes.position?.array||(v=!0)),!v)return p}this.cache.delete(u),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await x.catch(v=>(console.error(`Error loading LOD ${e} from ${a}
|
|
3
|
+
`,v),null)),p=!1;if(c==null||(c instanceof R&&t instanceof R?c.image?.data||c.source?.data?c=this.copySettings(t,c):(p=!0,this.cache.delete(u)):c instanceof W&&t instanceof W&&(c.attributes.position?.array||(p=!0,this.cache.delete(u)))),!p)return c}if(!y.use)return g&&console.log(`LOD ${e} was aborted: ${a}`),null;const w=n,T=new Promise(async(c,p)=>{if(ut){const h=await(await at({})).load(a);if(h.textures.length>0)for(const d of h.textures){let f=d.texture;return m.assignLODInformation(o.url,f,r,e,void 0),t instanceof R&&(f=this.copySettings(t,f)),f&&(f.guid=w.guid),c(f)}if(h.geometries.length>0){const d=new Array;for(const f of h.geometries){const _=f.geometry;m.assignLODInformation(o.url,_,r,e,f.primitiveIndex),d.push(_)}return c(d)}return c(null)}const v=new ie;ae(v),g&&(await new Promise(h=>setTimeout(h,1e3)),s&&console.warn("Start loading (delayed) "+a,w.guid));let A=a;if(w&&Array.isArray(w.lods)){const h=w.lods[e];h.hash&&(A+="?v="+h.hash)}const D=await v.loadAsync(A).catch(h=>(console.error(`Error loading LOD ${e} from ${a}
|
|
4
|
+
`,h),c(null)));if(!D)return c(null);const B=D.parser;s&&console.log("Loading finished "+a,w.guid);let M=0;if(D.parser.json.textures){let h=!1;for(const d of D.parser.json.textures){if(d?.extensions){const f=d?.extensions[$];if(f?.guid&&f.guid===w.guid){h=!0;break}}M++}if(h){let d=await B.getDependency("texture",M);return d&&m.assignLODInformation(o.url,d,r,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,M,d,u),t instanceof R&&(d=this.copySettings(t,d)),d&&(d.guid=w.guid),c(d)}else g&&console.warn("Could not find texture with guid",w.guid,D.parser.json)}if(M=0,D.parser.json.meshes){let h=!1;for(const d of D.parser.json.meshes){if(d?.extensions){const f=d?.extensions[$];if(f?.guid&&f.guid===w.guid){h=!0;break}}M++}if(h){const d=await B.getDependency("mesh",M);if(s&&console.log(`Loaded Mesh "${d.name}"`,a,M,d,u),d.isMesh===!0){const f=d.geometry;return m.assignLODInformation(o.url,f,r,e,0),c(f)}else{const f=new Array;for(let _=0;_<d.children.length;_++){const S=d.children[_];if(S.isMesh===!0){const G=S.geometry;m.assignLODInformation(o.url,G,r,e,_),f.push(G)}}return c(f)}}else g&&console.warn("Could not find mesh with guid",w.guid,D.parser.json)}return c(null)});this.cache.set(u,T),y.use(T);const b=await T;return b!=null?b instanceof R?(this.cache.set(u,new WeakRef(b)),m._resourceRegistry.register(b,u)):Array.isArray(b)?this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(b)):this.cache.set(u,Promise.resolve(null)),b}else if(t instanceof R){s&&console.log("Load texture from uri: "+a);const u=await new ze().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,s&&console.log(n,u)):g&&console.warn("failed loading",a),u}}return null}static maxConcurrent=50;static queue=new st(m.maxConcurrent,{debug:g!=!1});static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const n=new dt(t,s,o,r);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}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
|
|
4
5
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class ct{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.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 s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 k=z("debugprogressive"),ht=z("noprogressive"),pe=Symbol("Needle:LODSManager"),me=Symbol("Needle:LODState"),j=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class L{static debugDrawLine;static getObjectLODState(t){return t[me]}static addPlugin(t){N.push(t)}static removePlugin(t){const e=N.indexOf(t);e>=0&&N.splice(e,1)}static get(t,e){if(t[pe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[pe];const s=new L(t,{engine:"unknown",...e});return t[pe]=s,s}renderer;context;projectionScreenMatrix=new ve;get plugins(){return N}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 fe(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new qe;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;Z(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#i.getDelta(),e.#n+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,k&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const i=o[0].material;(i.name==="EffectMaterial"||i.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ht||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,k&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,k&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){k&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const x=Math.random()*16777215,y=new Ve({color:x});a.object.material=y}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const i=s.transparent;for(const a of i){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[me];if(r||(r=new gt,s[me]=r),r.frames++<2)return;for(const l of N)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;i>=0?(O.mesh_lod=i,O.texture_lod=i):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,i),g&&s.material&&!s.isGizmo&&Ce(s.material);for(const l of N)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[j]===void 0||e<t[j])&&(o=!0),s!==void 0&&s>=0&&(o=t[j]!=s,e=s),o){t[j]=e;const r=p.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[j]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[j]!=o,e=o),s){t[j]=e;const r=t.geometry,i=p.assignMeshLOD(t,e).then(l=>(l&&t[j]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new Le;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new ve;_tempWorldPosition=new C;_tempBoxSize=new C;_tempBox2Size=new C;static corner0=new C;static corner1=new C;static corner2=new C;static corner3=new C;static _tempPtInside=new C;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,i=(s.y+o.y)*.5;return this._tempPtInside.set(r,i,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let i=11,l=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=p.getMeshLODExtension(e.geometry)?.lods,u=p.getPrimitiveIndex(e.geometry),x=a&&a.length>0,y=p.getMaterialMinMaxLODsCount(e.material),w=y.min_count!==1/0&&y.min_count>=0&&y.max_count>=0;if(!x&&!w){r.mesh_lod=0,r.texture_lod=0;return}x||(l=!0,i=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const d=e;if(!d.boundingBox)d.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!d[L.$skinnedMeshBoundsOffset]){const v=L.skinnedMeshBoundsFrameOffsetCounter++;d[L.$skinnedMeshBoundsOffset]=v}const m=d[L.$skinnedMeshBoundsOffset];if((s.frames+m)%this.skinnedMeshAutoUpdateBoundsInterval===0){const v=V(d),A=d.geometry;v&&(d.geometry=v),d.computeBoundingBox(),d.geometry=A}}M=d.boundingBox}if(M){const d=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(M),this._tempBox.applyMatrix4(e.matrixWorld),d.isPerspectiveCamera&&L.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&d.isPerspectiveCamera&&d.fov>70){const h=this._tempBox.min,c=this._tempBox.max;let f=h.x,_=h.y,S=c.x,G=c.y;const H=2,re=1.5,Q=(h.x+c.x)*.5,J=(h.y+c.y)*.5;f=(f-Q)*H+Q,_=(_-J)*H+J,S=(S-Q)*H+Q,G=(G-J)*H+J;const $e=f<0&&S>0?0:Math.min(Math.abs(h.x),Math.abs(c.x)),Ge=_<0&&G>0?0:Math.min(Math.abs(h.y),Math.abs(c.y)),oe=Math.max($e,Ge);s.lastCentrality=(re-oe)*(re-oe)*(re-oe)}else s.lastCentrality=1;const m=this._tempBox.getSize(this._tempBoxSize);m.multiplyScalar(.5),screen.availHeight>0&&T>0&&m.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?m.x*=t.aspect:t.isOrthographicCamera;const v=t.matrixWorldInverse,A=this._tempBox2;A.copy(M),A.applyMatrix4(e.matrixWorld),A.applyMatrix4(v);const b=A.getSize(this._tempBox2Size),R=Math.max(b.x,b.y);if(Math.max(m.x,m.y)!=0&&R!=0&&(m.z=b.z/Math.max(b.x,b.y)*Math.max(m.x,m.y)),s.lastScreenCoverage=Math.max(m.x,m.y,m.z),s.lastScreenspaceVolume.copy(m),s.lastScreenCoverage*=s.lastCentrality,k&&L.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const c=L.corner0,f=L.corner1,_=L.corner2,S=L.corner3;c.copy(this._tempBox.min),f.copy(this._tempBox.max),f.x=c.x,_.copy(this._tempBox.max),_.y=c.y,S.copy(this._tempBox.max);const G=(c.z+S.z)*.5;c.z=f.z=_.z=S.z=G,c.applyMatrix4(h),f.applyMatrix4(h),_.applyMatrix4(h),S.applyMatrix4(h),L.debugDrawLine(c,f,255),L.debugDrawLine(c,_,255),L.debugDrawLine(f,S,255),L.debugDrawLine(_,S,255)}let D=999;if(a&&s.lastScreenCoverage>0)for(let h=0;h<a.length;h++){const c=a[h],f=(c.densities?.[u]||c.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!c.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),f<o){D=h;break}}D<i&&(i=D,l=!0)}if(l?r.mesh_lod=i:r.mesh_lod=s.lastLodLevel_Mesh,k&&r.mesh_lod!=s.lastLodLevel_Mesh){const d=a?.[r.mesh_lod];d&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${d.densities?.[u].toFixed(0)}) | ${e.name}`)}if(w){const d="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=y.max_count-1,k){const m=y.lods[y.max_count-1];k&&console.log(`First Texture LOD ${r.texture_lod} (${m.max_height}px) - ${e.name}`)}}else{const m=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let v=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(v*=1.5);const A=T/window.devicePixelRatio*v;let b=!1;for(let R=y.lods.length-1;R>=0;R--){const D=y.lods[R];if(!(d&&D.max_height>=2048)&&!(Se()&&D.max_height>4096)&&(D.max_height>A||!b&&R===0)){if(b=!0,r.texture_lod=R,k&&r.texture_lod<s.lastLodLevel_Texture){const h=D.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${h}px
|
|
6
|
-
Screensize: ${A.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${
|
|
7
|
-
${e.name}`)}break}}}}else r.texture_lod=0}}class gt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new C;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),se=Symbol("NEEDLE_texture_lod");let
|
|
8
|
-
`,
|
|
6
|
+
`,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 dt{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.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 s=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=s,this.ready=new Promise(o=>{this._resolve=o}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 k=z("debugprogressive"),ht=z("noprogressive"),me=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),j=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};class L{static debugDrawLine;static getObjectLODState(t){return t[pe]}static addPlugin(t){N.push(t)}static removePlugin(t){const e=N.indexOf(t);e>=0&&N.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const s=new L(t,{engine:"unknown",...e});return t[me]=s,s}renderer;context;projectionScreenMatrix=new ve;get plugins(){return N}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 fe(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new qe;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;Z(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,k&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let r=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(r=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(r=!1),r){if(ht||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,k&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,k&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){k&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(k==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const y=Math.random()*16777215,x=new Ve({color:y});a.object.material=x}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[pe];if(r||(r=new gt,s[pe]=r),r.frames++<2)return;for(const l of N)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;n>=0?(O.mesh_lod=n,O.texture_lod=n):(this.calculateLodLevel(e,s,r,o,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(s,O.mesh_lod),s.material&&O.texture_lod>=0&&this.loadProgressiveTextures(s.material,O.texture_lod,n),g&&s.material&&!s.isGizmo&&Ce(s.material);for(const l of N)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,O);r.lastLodLevel_Mesh=O.mesh_lod,r.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[j]===void 0||e<t[j])&&(o=!0),s!==void 0&&s>=0&&(o=t[j]!=s,e=s),o){t[j]=e;const r=m.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[j]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[j]!=o,e=o),s){t[j]=e;const r=t.geometry,n=m.assignMeshLOD(t,e).then(l=>(l&&t[j]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new Le;_tempBox=new ne;_tempBox2=new ne;tempMatrix=new ve;_tempWorldPosition=new C;_tempBoxSize=new C;_tempBox2Size=new C;static corner0=new C;static corner1=new C;static corner2=new C;static corner3=new C;static _tempPtInside=new C;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=11,l=!1;if(k&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=m.getMeshLODExtension(e.geometry)?.lods,u=m.getPrimitiveIndex(e.geometry),y=a&&a.length>0,x=m.getMaterialMinMaxLODsCount(e.material),w=x.min_count!==1/0&&x.min_count>=0&&x.max_count>=0;if(!y&&!w){r.mesh_lod=0,r.texture_lod=0;return}y||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let b=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[L.$skinnedMeshBoundsOffset]){const v=L.skinnedMeshBoundsFrameOffsetCounter++;c[L.$skinnedMeshBoundsOffset]=v}const p=c[L.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const v=V(c),A=c.geometry;v&&(c.geometry=v),c.computeBoundingBox(),c.geometry=A}}b=c.boundingBox}if(b){const c=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(b),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&L.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const h=this._tempBox.min,d=this._tempBox.max;let f=h.x,_=h.y,S=d.x,G=d.y;const H=2,re=1.5,Q=(h.x+d.x)*.5,J=(h.y+d.y)*.5;f=(f-Q)*H+Q,_=(_-J)*H+J,S=(S-Q)*H+Q,G=(G-J)*H+J;const $e=f<0&&S>0?0:Math.min(Math.abs(h.x),Math.abs(d.x)),Ge=_<0&&G>0?0:Math.min(Math.abs(h.y),Math.abs(d.y)),oe=Math.max($e,Ge);s.lastCentrality=(re-oe)*(re-oe)*(re-oe)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&T>0&&p.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const v=t.matrixWorldInverse,A=this._tempBox2;A.copy(b),A.applyMatrix4(e.matrixWorld),A.applyMatrix4(v);const D=A.getSize(this._tempBox2Size),B=Math.max(D.x,D.y);if(Math.max(p.x,p.y)!=0&&B!=0&&(p.z=D.z/Math.max(D.x,D.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,k&&L.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const d=L.corner0,f=L.corner1,_=L.corner2,S=L.corner3;d.copy(this._tempBox.min),f.copy(this._tempBox.max),f.x=d.x,_.copy(this._tempBox.max),_.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=f.z=_.z=S.z=G,d.applyMatrix4(h),f.applyMatrix4(h),_.applyMatrix4(h),S.applyMatrix4(h),L.debugDrawLine(d,f,255),L.debugDrawLine(d,_,255),L.debugDrawLine(f,S,255),L.debugDrawLine(_,S,255)}let M=999;if(a&&s.lastScreenCoverage>0)for(let h=0;h<a.length;h++){const d=a[h],f=(d.densities?.[u]||d.density||1e-5)/s.lastScreenCoverage;if(u>0&&Pe()&&!d.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),f<o){M=h;break}}M<n&&(n=M,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,k&&r.mesh_lod!=s.lastLodLevel_Mesh){const c=a?.[r.mesh_lod];c&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} \u2192 ${r.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(w){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=x.max_count-1,k){const p=x.lods[x.max_count-1];k&&console.log(`First Texture LOD ${r.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let v=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(v*=1.5);const A=T/window.devicePixelRatio*v;let D=!1;for(let B=x.lods.length-1;B>=0;B--){const M=x.lods[B];if(!(c&&M.max_height>=2048)&&!(Se()&&M.max_height>4096)&&(M.max_height>A||!D&&B===0)){if(D=!0,r.texture_lod=B,k&&r.texture_lod<s.lastLodLevel_Texture){const h=M.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} \u2192 ${r.texture_lod} = ${h}px
|
|
7
|
+
Screensize: ${A.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${p.toFixed(1)}
|
|
8
|
+
${e.name}`)}break}}}}else r.texture_lod=0}}class gt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new C;lastCentrality=0}const Ie=Symbol("NEEDLE_mesh_lod"),se=Symbol("NEEDLE_texture_lod");let xe=null;function ye(){const i=ft();i&&(i.mapURLs(function(t){return Ee(),t}),Ee(),xe?.disconnect(),xe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&Be(s)})})}),xe.observe(document,{childList:!0,subtree:!0}))}function ft(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),ye()}),null)}function Ee(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{Be(i)})}const ke=new WeakSet;let mt=0;function Be(i){if(!i||ke.has(i))return null;ke.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++mt+`
|
|
9
|
+
`,i.getAttribute("src"));let t=null,e=null,s=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),n=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!s&&a!=null&&(s=i[a])}if(t&&e){let o=function(){if(s){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=L.get(t,{engine:"model-viewer"});return L.addPlugin(new pt),r.enable(),r.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}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[se]==!0)return;e[se]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let n=function(a){if(a[se]==!0)return;a[se]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let y=0;y<u.length;y++){const x=u[y],w=a[x];if(w?.isTexture===!0){const T=w.userData?.associations?.textures;if(T==null)continue;const b=s.parser.json.textures[T];if(!b){console.warn("Texture data not found for texture index "+T);continue}if(b?.extensions?.[$]){const c=b.extensions[$];c&&r&&m.registerTexture(r,w,c.lods.length,T,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ie]==!0)return;e[Ie]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[$];if(r&&o){const n=e.uuid;m.registerMesh(o,n,e,0,r.lods.length,r)}}}function Re(...i){let t,e,s,o;switch(i.length){case 2:[s,e]=i,o={};break;case 3:[s,e,o]=i;break;case 4:[t,e,s,o]=i;break;default:throw new Error("Invalid arguments")}Z(e),ae(s),ce(s,{progressive:!0,...o?.hints}),s.register(n=>new m(n));const r=L.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}if(ye(),!rt){const i={gltfProgressive:{useNeedleProgressive:Re,LODsManager:L,configureLoader:ce,getRaycastMesh:V,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{$ as EXTENSION_NAME,L as LODsManager,m as NEEDLE_progressive,_e as VERSION,ae as addDracoAndKTX2Loaders,ce as configureLoader,Z as createLoaders,V as getRaycastMesh,ye as patchModelViewer,Te as registerRaycastMesh,De as setDracoDecoderLocation,Me as setKTX2TranscoderLocation,Re as useNeedleProgressive,Ae as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
"use strict";var Fe=Object.create;var Le=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var Ue=Object.getOwnPropertyNames;var ze=Object.getPrototypeOf,Ve=Object.prototype.hasOwnProperty;var Ne=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Ue(t))!Ve.call(i,o)&&o!==e&&Le(i,o,{get:()=>t[o],enumerable:!(s=We(t,o))||s.enumerable});return i};var qe=(i,t,e)=>(e=i!=null?Fe(ze(i)):{},Ne(t||!i||!i.__esModule?Le(e,"default",{value:i,enumerable:!0}):e,i));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("three"),pe=require("three/examples/jsm/loaders/GLTFLoader.js"),Ee=require("three/examples/jsm/libs/meshopt_decoder.module.js"),je=require("three/examples/jsm/loaders/DRACOLoader.js"),Xe=require("three/examples/jsm/loaders/KTX2Loader.js");var ie=typeof document<"u"?document.currentScript:null;const De="";globalThis.GLTF_PROGRESSIVE_VERSION=De;console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",N="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ke=k,Ye=N,Oe=new URL(k+"draco_decoder.js");Oe.searchParams.append("range","true");fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ke&&Se("./include/draco/"),N===Ye&&Te("./include/ktx2/")}).finally(()=>{Pe()});const He=()=>({dracoDecoderPath:k,ktx2TranscoderPath:N});function Se(i){k=i,C&&C[fe]!=k?(console.debug("Updating Draco decoder path to "+i),C[fe]=k,C.setDecoderPath(k),C.preload()):console.debug("Setting Draco decoder path to "+i)}function Te(i){N=i,B&&B.transcoderPath!=N?(console.debug("Updating KTX2 transcoder path to "+i),B.setTranscoderPath(N),B.init()):console.debug("Setting KTX2 transcoder path to "+i)}function se(i){return Pe(),i?B.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:C,ktx2Loader:B,meshoptDecoder:te}}function me(i){i.dracoLoader||i.setDRACOLoader(C),i.ktx2Loader||i.setKTX2Loader(B),i.meshoptDecoder||i.setMeshoptDecoder(te)}const fe=Symbol("dracoDecoderPath");let C,te,B;function Pe(){C||(C=new je.DRACOLoader,C[fe]=k,C.setDecoderPath(k),C.setDecoderConfig({type:"js"}),C.preload()),B||(B=new Xe.KTX2Loader,B.setTranscoderPath(N),B.init()),te||(te=Ee.MeshoptDecoder)}const he=new WeakMap;function ye(i,t){let e=he.get(i);e?e=Object.assign(e,t):e=t,he.set(i,e)}const Qe=pe.GLTFLoader.prototype.load;function Je(...i){const t=he.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const r=t?.progressive!==void 0?t.progressive:!0,n=t?.usecase?t.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${n}`:this.requestHeader.Accept=`*/*;usecase=${n}`,e=s.toString()}return i[0]=e,Qe?.call(this,...i)}pe.GLTFLoader.prototype.load=Je;q("debugprogressive");function q(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Ze(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Ae(){return X!==void 0||(X=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),q("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",X)),X}let X;function _e(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class et{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}const tt=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function E(i){return i?.[ge]instanceof d.BufferGeometry?i[ge]:null}function Ce(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!E(i)){const s=st(t);s.userData={isRaycastMesh:!0},i[ge]=s}}function Ie(i=!0){if(i){if(K)return;const t=K=d.Mesh.prototype.raycast;d.Mesh.prototype.raycast=function(e,s){const o=this,r=E(o);let n;r&&o.isMesh&&(n=o.geometry,o.geometry=r),t.call(this,e,s),n&&(o.geometry=n)}}else{if(!K)return;d.Mesh.prototype.raycast=K,K=null}}let K=null;function st(i){const t=new d.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const U=new Array,p=q("debugprogressive");let H,V=-1;if(p){let i=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(H=!H,console.log(`Toggle wireframe [${H}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(V=s,console.log(`Set LOD level to [${V}]`))})}function Re(i){if(p&&H!==void 0)if(Array.isArray(i))for(const t of i)Re(t);else i&&"wireframe"in i&&(i.wireframe=H===!0)}const Y=new Array;let rt=0;const ot=Ae()?2:10;function it(i){if(Y.length<ot){const s=Y.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const o=xe.createWorker(i||{});return Y.push(o),o}const t=rt++%Y.length;return Y[t]}class xe{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){nt(o.result),r.resolve(o.result);const n=r.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},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/loader.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ie&&ie.tagName.toUpperCase()==="SCRIPT"&&ie.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new xe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=He();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),o=await this._webglRenderer);const l=se(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function nt(i){for(const t of i.geometries){const e=t.geometry,s=new d.BufferGeometry;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(ne(o))}for(const o in e.attributes){const r=e.attributes[o],n=ne(r);s.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const n=e.morphAttributes[o].map(l=>ne(l));s.morphAttributes[o]=n}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 o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;s=new d.CompressedTexture(o,r,n,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 i}function ne(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,o=new d.InterleavedBuffer(s,e.stride);t=new d.InterleavedBufferAttribute(o,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new d.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const at=q("gltf-progressive-worker"),lt=q("gltf-progressive-reduce-mipmaps"),ae=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class y{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,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=e,e}if(p==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=s.getAssignedLODInformation(l);if(u){const c=s.lodInfos.get(u.key);if(c&&c.lods){a.min_count=Math.min(a.min_count,c.lods.length),a.max_count=Math.max(a.max_count,c.lods.length);for(let x=0;x<c.lods.length;x++){const L=c.lods[x];L.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,L.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,L.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const n=t[r];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof d.Mesh||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of U)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,y.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const n=o.index||0;r=r[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:p&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const n=this.assignTextureLOD(r,e);o.push(n)}return Promise.all(o).then(r=>{const n=new Array;for(const l of r)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const n=s;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(c=>(c&&n.uniforms[l].value!=c&&(n.uniforms[l].value=c,n.uniformsNeedUpdate=!0),c));o.push(u),r.push(l)}}}else for(const n of Object.keys(s)){const l=s[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,n);o.push(a),r.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],c=r[a];u&&u.isTexture===!0?l.push({material:s,slot:c,texture:u,level:e}):l.push({material:s,slot:c,texture:null,level:e})}return l})}if(t instanceof d.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):y.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const n=s[o];if(n&&!p){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,n,r),null}if(lt&&r.mipmaps){const l=r.mipmaps.length;r.mipmaps.length=Math.min(r.mipmaps.length,3),l!==r.mipmaps.length&&p&&console.debug(`Reduced mipmap count from ${l} to ${r.mipmaps.length} for ${r.uuid}: ${r.image?.width}x${r.image?.height}.`)}s[o]=r}return r}else p=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}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&&y.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 o=e?.extensions[F];if(o){if(!o.lods){p&&console.warn("Texture has no LODs",o);return}let r=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===s&&(r=!0,y.registerTexture(this.url,n,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(n=>{n&&y.registerTexture(this.url,n,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[F];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const n=this.parser.associations.get(r);n?.meshes===s&&y.registerMesh(this.url,o.guid,r,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){p&&console.error("!! gltf-progressive: Called register texture without texture");return}if(p){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[ae]=r);const n=r.guid;y.assignLODInformation(t,e,n,s,o),y.lodInfos.set(n,r),y.lowresCache.set(n,new WeakRef(e))};static registerMesh=(t,e,s,o,r,n)=>{const l=s.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},n,s),y.assignLODInformation(t,l,e,o,r),y.lodInfos.set(e,n);let u=y.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],y.lowresCache.set(e,new WeakRef(u)),o>0&&!E(s)&&Ce(s,l);for(const c of U)c.onRegisteredNewMesh?.(s,n)};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)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.previouslyLoaded)s.includes(t)&&(this._disposeCacheEntry(o),this.previouslyLoaded.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.previouslyLoaded)this._disposeCacheEntry(e);this.previouslyLoaded.clear()}}static _disposeCacheEntry(t){t instanceof WeakRef?t.deref()?.dispose():t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.dispose()}).catch(()=>{})}static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=y.previouslyLoaded.get(t);console.debug(`[gltf-progressive] FinalizationRegistry cleanup: Resource GC'd for ${t}.`),e instanceof WeakRef&&(e.deref()||(y.previouslyLoaded.delete(t),p&&console.log(`[gltf-progressive] Cache entry auto-cleaned (GC'd): ${t}`)))});static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=p=="verbose",o=this.getAssignedLODInformation(t);if(!o)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let n;if(t.isTexture===!0){const a=t;a.source&&a.source[ae]&&(n=a.source[ae])}if(n||(n=y.lodInfos.get(r)),!n)p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,y.lodInfos);else{if(e>0){let c=!1;const x=Array.isArray(n.lods);if(x&&e>=n.lods.length?c=!0:x||(c=!0),c){const L=this.lowresCache.get(r);if(L){const _=L.deref();if(_)return _;this.lowresCache.delete(r),p&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const a=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!a)return p&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const u=Ze(o.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const c=u+"_"+n.guid,x=await this.queue.slot(u),L=this.previouslyLoaded.get(c);if(L!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${c}`),L instanceof WeakRef){const f=L.deref();if(f){let g=f,b=!1;if(g instanceof d.Texture&&t instanceof d.Texture?g.image?.data||g.source?.data?g=this.copySettings(t,g):b=!0:g instanceof d.BufferGeometry&&t instanceof d.BufferGeometry&&(g.attributes.position?.array||(b=!0)),!b)return g}this.previouslyLoaded.delete(c),p&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${c}`)}else{let f=await L.catch(b=>(console.error(`Error loading LOD ${e} from ${u}
|
|
2
|
-
|
|
3
|
-
`,
|
|
1
|
+
"use strict";var Fe=Object.create;var _e=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var Ue=Object.getOwnPropertyNames;var ze=Object.getPrototypeOf,Ve=Object.prototype.hasOwnProperty;var Ne=(n,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Ue(t))!Ve.call(n,o)&&o!==e&&_e(n,o,{get:()=>t[o],enumerable:!(s=We(t,o))||s.enumerable});return n};var qe=(n,t,e)=>(e=n!=null?Fe(ze(n)):{},Ne(t||!n||!n.__esModule?_e(e,"default",{value:n,enumerable:!0}):e,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("three"),pe=require("three/examples/jsm/loaders/GLTFLoader.js"),Ee=require("three/examples/jsm/libs/meshopt_decoder.module.js"),je=require("three/examples/jsm/loaders/DRACOLoader.js"),Xe=require("three/examples/jsm/loaders/KTX2Loader.js");var ne=typeof document<"u"?document.currentScript:null;const De="";globalThis.GLTF_PROGRESSIVE_VERSION=De;console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",N="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ke=k,Ye=N,Oe=new URL(k+"draco_decoder.js");Oe.searchParams.append("range","true");fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ke&&Se("./include/draco/"),N===Ye&&Te("./include/ktx2/")}).finally(()=>{Pe()});const He=()=>({dracoDecoderPath:k,ktx2TranscoderPath:N});function Se(n){k=n,C&&C[fe]!=k?(console.debug("Updating Draco decoder path to "+n),C[fe]=k,C.setDecoderPath(k),C.preload()):console.debug("Setting Draco decoder path to "+n)}function Te(n){N=n,B&&B.transcoderPath!=N?(console.debug("Updating KTX2 transcoder path to "+n),B.setTranscoderPath(N),B.init()):console.debug("Setting KTX2 transcoder path to "+n)}function se(n){return Pe(),n?B.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:C,ktx2Loader:B,meshoptDecoder:te}}function me(n){n.dracoLoader||n.setDRACOLoader(C),n.ktx2Loader||n.setKTX2Loader(B),n.meshoptDecoder||n.setMeshoptDecoder(te)}const fe=Symbol("dracoDecoderPath");let C,te,B;function Pe(){C||(C=new je.DRACOLoader,C[fe]=k,C.setDecoderPath(k),C.setDecoderConfig({type:"js"}),C.preload()),B||(B=new Xe.KTX2Loader,B.setTranscoderPath(N),B.init()),te||(te=Ee.MeshoptDecoder)}const he=new WeakMap;function ye(n,t){let e=he.get(n);e?e=Object.assign(e,t):e=t,he.set(n,e)}const Qe=pe.GLTFLoader.prototype.load;function Je(...n){const t=he.get(this);let e=n[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const r=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";r?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=s.toString()}return n[0]=e,Qe?.call(this,...n)}pe.GLTFLoader.prototype.load=Je;q("debugprogressive");function q(n){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Ze(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const s=n.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Ae(){return X!==void 0||(X=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),q("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",X)),X}let X;function Le(){if(typeof window>"u")return!1;const n=new URL(window.location.href),t=n.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(n.hostname);return n.hostname==="127.0.0.1"||t}class et{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:o}=this._queue.shift();o({use:r=>this.add(s,r)})}}}const tt=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function E(n){return n?.[ge]instanceof c.BufferGeometry?n[ge]:null}function Ce(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!E(n)){const s=st(t);s.userData={isRaycastMesh:!0},n[ge]=s}}function Ie(n=!0){if(n){if(K)return;const t=K=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const o=this,r=E(o);let i;r&&o.isMesh&&(i=o.geometry,o.geometry=r),t.call(this,e,s),i&&(o.geometry=i)}}else{if(!K)return;c.Mesh.prototype.raycast=K,K=null}}let K=null;function st(n){const t=new c.BufferGeometry;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const U=new Array,g=q("debugprogressive");let H,V=-1;if(g){let n=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(H=!H,console.log(`Toggle wireframe [${H}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(V=s,console.log(`Set LOD level to [${V}]`))})}function Re(n){if(g&&H!==void 0)if(Array.isArray(n))for(const t of n)Re(t);else n&&"wireframe"in n&&(n.wireframe=H===!0)}const Y=new Array;let rt=0;const ot=Ae()?2:10;function nt(n){if(Y.length<ot){const s=Y.length;g&&console.warn(`[Worker] Creating new worker #${s}`);const o=xe.createWorker(n||{});return Y.push(o),o}const t=rt++%Y.length;return Y[t]}class xe{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const o=s.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const r of this._running)if(r.url===o.result.url){it(o.result),r.resolve(o.result);const i=r.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/loader.worker-CCrD-Ycm.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ne&&ne.tagName.toUpperCase()==="SCRIPT"&&ne.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new xe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=He();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),o=await this._webglRenderer);const l=se(o).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function it(n){for(const t of n.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const o=e.index;s.setIndex(ie(o))}for(const o in e.attributes){const r=e.attributes[o],i=ie(r);s.setAttribute(o,i)}if(e.morphAttributes)for(const o in e.morphAttributes){const i=e.morphAttributes[o].map(l=>ie(l));s.morphAttributes[o]=i}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)s.addGroup(o.start,o.count,o.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of n.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const o=e.mipmaps,r=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(o,r,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.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 c.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return n}function ie(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,s=e.array,o=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(o,n.itemSize,s.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new c.BufferAttribute(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const at=q("gltf-progressive-worker"),lt=q("gltf-progressive-reduce-mipmaps"),ae=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class y{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,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[o]=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 u=l.uniforms[a].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function i(l,a){const u=s.getAssignedLODInformation(l);if(u){const d=s.lodInfos.get(u.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 x=0;x<d.lods.length;x++){const _=d.lods[x];_.width&&(a.lods[x]=a.lods[x]||{min_height:1/0,max_height:0},a.lods[x].min_height=Math.min(a.lods[x].min_height,_.height),a.lods[x].max_height=Math.max(a.lods[x].max_height,_.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const r of t)if(this.hasLODLevelAvailable(r,e))return!0;return!1}if(t.isMaterial===!0){for(const r of Object.keys(t)){const i=t[r];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const r of t.children)if(r.isMesh===!0&&this.hasLODLevelAvailable(r,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const r=s.userData.LODS;if(o=this.lodInfos.get(r.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const r of U)r.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,y.getOrLoadLOD(s,e).then(r=>{if(Array.isArray(r)){const i=o.index||0;r=r[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],r&&s!=r&&(r?.isBufferGeometry?t.geometry=r:g&&console.error("Invalid LOD geometry",r))),r}).catch(r=>(console.error("Error loading mesh LOD",t,r),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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const o=new Array;for(const r of s.material){const i=this.assignTextureLOD(r,e);o.push(i)}return Promise.all(o).then(r=>{const i=new Array;for(const l of r)Array.isArray(l)&&i.push(...l);return i})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,o=[],r=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const i=s;for(const l of Object.keys(i.uniforms)){const a=i.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,s,l).then(d=>(d&&i.uniforms[l].value!=d&&(i.uniforms[l].value=d,i.uniformsNeedUpdate=!0),d));o.push(u),r.push(l)}}}else for(const i of Object.keys(s)){const l=s[i];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,s,i);o.push(a),r.push(i)}}return Promise.all(o).then(i=>{const l=new Array;for(let a=0;a<i.length;a++){const u=i[a],d=r[a];u&&u.isTexture===!0?l.push({material:s,slot:d,texture:u,level:e}):l.push({material:s,slot:d,texture:null,level:e})}return l})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):y.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(r?.isTexture===!0){if(r!=t&&s&&o){const i=s[o];if(i&&!g){const l=this.getAssignedLODInformation(i);if(l&&l?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,s,i,r),null}if(lt&&r.mipmaps){const l=r.mipmaps.length;r.mipmaps.length=Math.min(r.mipmaps.length,3),l!==r.mipmaps.length&&g&&console.debug(`Reduced mipmap count from ${l} to ${r.mipmaps.length} for ${r.uuid}: ${r.image?.width}x${r.image?.height}.`)}s[o]=r}return r}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&y.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[F];if(o){if(!o.lods){g&&console.warn("Texture has no LODs",o);return}let r=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===s&&(r=!0,y.registerTexture(this.url,i,o.lods?.length,s,o));r||this.parser.getDependency("texture",s).then(i=>{i&&y.registerTexture(this.url,i,o.lods?.length,s,o)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const o=e?.extensions[F];if(o&&o.lods){for(const r of this.parser.associations.keys())if(r.isMesh){const i=this.parser.associations.get(r);i?.meshes===s&&y.registerMesh(this.url,o.guid,r,o.lods.length,i.primitives,o)}}}}),null}static registerTexture=(t,e,s,o,r)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`,r,e)}e.source&&(e.source[ae]=r);const i=r.guid;y.assignLODInformation(t,e,i,s,o),y.lodInfos.set(i,r),y.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,o,r,i)=>{const l=s.geometry;if(!l){g&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:r,uuid:s.uuid},i,s),y.assignLODInformation(t,l,e,o,r),y.lodInfos.set(e,i);let u=y.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],y.lowresCache.set(e,new WeakRef(u)),o>0&&!E(s)&&Ce(s,l);for(const d of U)d.onRegisteredNewMesh?.(s,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}this.lowresCache.delete(t)}for(const[s,o]of this.cache)s.includes(t)&&(this._disposeCacheEntry(o),this.cache.delete(s))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture)s.dispose();else if(Array.isArray(s))for(const o of s)o.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear()}}static _disposeCacheEntry(t){t instanceof WeakRef?t.deref()?.dispose():t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=y.cache.get(t);g&&console.debug(`[gltf-progressive] Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(y.cache.delete(t),g&&console.log(`[gltf-progressive] Cache entry auto-cleaned (GC'd): ${t}`)))});static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const r=o?.key;let i;if(t.isTexture===!0){const a=t;a.source&&a.source[ae]&&(i=a.source[ae])}if(i||(i=y.lodInfos.get(r)),!i)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type,y.lodInfos);else{if(e>0){let d=!1;const x=Array.isArray(i.lods);if(x&&e>=i.lods.length?d=!0:x||(d=!0),d){const _=this.lowresCache.get(r);if(_){const L=_.deref();if(L)return L;this.lowresCache.delete(r),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${r}`)}return null}}const a=Array.isArray(i.lods)?i.lods[e]?.path:i.lods;if(!a)return g&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const u=Ze(o.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const d=u+"_"+i.guid,x=await this.queue.slot(u),_=this.cache.get(d);if(_!==void 0)if(s&&console.log(`LOD ${e} was already loading/loaded: ${d}`),_ instanceof WeakRef){const f=_.deref();if(f){let p=f,b=!1;if(p instanceof c.Texture&&t instanceof c.Texture?p.image?.data||p.source?.data?p=this.copySettings(t,p):b=!0:p instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(p.attributes.position?.array||(b=!0)),!b)return p}this.cache.delete(d),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${d}`)}else{let f=await _.catch(b=>(console.error(`Error loading LOD ${e} from ${u}
|
|
3
|
+
`,b),null)),p=!1;if(f==null||(f instanceof c.Texture&&t instanceof c.Texture?f.image?.data||f.source?.data?f=this.copySettings(t,f):(p=!0,this.cache.delete(d)):f instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(f.attributes.position?.array||(p=!0,this.cache.delete(d)))),!p)return f}if(!x.use)return g&&console.log(`LOD ${e} was aborted: ${u}`),null;const L=i,I=new Promise(async(f,p)=>{if(at){const w=await(await nt({})).load(u);if(w.textures.length>0)for(const h of w.textures){let m=h.texture;return y.assignLODInformation(o.url,m,r,e,void 0),t instanceof c.Texture&&(m=this.copySettings(t,m)),m&&(m.guid=L.guid),f(m)}if(w.geometries.length>0){const h=new Array;for(const m of w.geometries){const S=m.geometry;y.assignLODInformation(o.url,S,r,e,m.primitiveIndex),h.push(S)}return f(h)}return f(null)}const b=new pe.GLTFLoader;me(b),g&&(await new Promise(v=>setTimeout(v,1e3)),s&&console.warn("Start loading (delayed) "+u,L.guid));let $=u;if(L&&Array.isArray(L.lods)){const v=L.lods[e];v.hash&&($+="?v="+v.hash)}const O=await b.loadAsync($).catch(v=>(console.error(`Error loading LOD ${e} from ${u}
|
|
4
|
+
`,v),f(null)));if(!O)return f(null);const z=O.parser;s&&console.log("Loading finished "+u,L.guid);let P=0;if(O.parser.json.textures){let v=!1;for(const w of O.parser.json.textures){if(w?.extensions){const h=w?.extensions[F];if(h?.guid&&h.guid===L.guid){v=!0;break}}P++}if(v){let w=await z.getDependency("texture",P);return w&&y.assignLODInformation(o.url,w,r,e,void 0),s&&console.log('change "'+t.name+'" → "'+w.name+'"',u,P,w,d),t instanceof c.Texture&&(w=this.copySettings(t,w)),w&&(w.guid=L.guid),f(w)}else g&&console.warn("Could not find texture with guid",L.guid,O.parser.json)}if(P=0,O.parser.json.meshes){let v=!1;for(const w of O.parser.json.meshes){if(w?.extensions){const h=w?.extensions[F];if(h?.guid&&h.guid===L.guid){v=!0;break}}P++}if(v){const w=await z.getDependency("mesh",P);if(s&&console.log(`Loaded Mesh "${w.name}"`,u,P,w,d),w.isMesh===!0){const h=w.geometry;return y.assignLODInformation(o.url,h,r,e,0),f(h)}else{const h=new Array;for(let m=0;m<w.children.length;m++){const S=w.children[m];if(S.isMesh===!0){const T=S.geometry;y.assignLODInformation(o.url,T,r,e,m),h.push(T)}}return f(h)}}else g&&console.warn("Could not find mesh with guid",L.guid,O.parser.json)}return f(null)});this.cache.set(d,I),x.use(I);const D=await I;return D!=null?D instanceof c.Texture?(this.cache.set(d,new WeakRef(D)),y._resourceRegistry.register(D,d)):Array.isArray(D)?this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(D)):this.cache.set(d,Promise.resolve(null)),D}else if(t instanceof c.Texture){s&&console.log("Load texture from uri: "+u);const x=await new c.TextureLoader().loadAsync(u);return x?(x.guid=i.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,s&&console.log(i,x)):g&&console.warn("failed loading",u),x}}return null}static maxConcurrent=50;static queue=new et(y.maxConcurrent,{debug:g!=!1});static assignLODInformation(t,e,s,o,r){if(!e)return;e.userData||(e.userData={});const i=new ut(t,s,o,r);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
|
|
4
5
|
`,t.uuid,`
|
|
5
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class ut{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class le{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.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 o=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=o,this.ready=new Promise(r=>{this._resolve=r}),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)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){p&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 R=q("debugprogressive"),dt=q("noprogressive"),ue=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),A={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const s=new M(t,{engine:"unknown",...e});return t[ue]=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 le(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),_e()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#i=new d.Clock;#r=0;#o=0;#n=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;se(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#i.getDelta(),e.#n+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,R&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const n=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,n)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const r=this.renderer.renderLists.get(t,0).opaque;let n=!0;if(r.length===1){const l=r[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(n=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(n=!1),n){if(dt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,R&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,R&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){R&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(R==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const c=Math.random()*16777215,x=new d.MeshStandardMaterial({color:c});a.object.material=x}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}const n=s.transparent;for(const a of n){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[de];if(r||(r=new ct,s[de]=r),r.frames++<2)return;for(const l of U)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;n>=0?(A.mesh_lod=n,A.texture_lod=n):(this.calculateLodLevel(e,s,r,o,A),A.mesh_lod=Math.round(A.mesh_lod),A.texture_lod=Math.round(A.texture_lod)),A.mesh_lod>=0&&this.loadProgressiveMeshes(s,A.mesh_lod),s.material&&A.texture_lod>=0&&this.loadProgressiveTextures(s.material,A.texture_lod,n),p&&s.material&&!s.isGizmo&&Re(s.material);for(const l of U)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,A);r.lastLodLevel_Mesh=A.mesh_lod,r.lastLodLevel_Texture=A.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=y.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,n=y.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return le.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,n=(s.y+o.y)*.5;return this._tempPtInside.set(r,n,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let l=10+1,a=!1;if(R&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=y.getMeshLODExtension(e.geometry)?.lods,c=y.getPrimitiveIndex(e.geometry),x=u&&u.length>0,L=y.getMaterialMinMaxLODsCount(e.material),_=L.min_count!==1/0&&L.min_count>=0&&L.max_count>=0;if(!x&&!_){r.mesh_lod=0,r.texture_lod=0;return}x||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let S=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const g=f[M.$skinnedMeshBoundsOffset];if((s.frames+g)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=E(f),$=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=$}}S=f.boundingBox}if(S){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(S),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const h=this._tempBox.min,m=this._tempBox.max;let O=h.x,T=h.y,G=m.x,j=m.y;const Q=2,re=1.5,J=(h.x+m.x)*.5,Z=(h.y+m.y)*.5;O=(O-J)*Q+J,T=(T-Z)*Q+Z,G=(G-J)*Q+J,j=(j-Z)*Q+Z;const $e=O<0&&G>0?0:Math.min(Math.abs(h.x),Math.abs(m.x)),Ge=T<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(m.y)),oe=Math.max($e,Ge);s.lastCentrality=(re-oe)*(re-oe)*(re-oe)}else s.lastCentrality=1;const g=this._tempBox.getSize(this._tempBoxSize);g.multiplyScalar(.5),screen.availHeight>0&&I>0&&g.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?g.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,$=this._tempBox2;$.copy(S),$.applyMatrix4(e.matrixWorld),$.applyMatrix4(b);const D=$.getSize(this._tempBox2Size),z=Math.max(D.x,D.y);if(Math.max(g.x,g.y)!=0&&z!=0&&(g.z=D.z/Math.max(D.x,D.y)*Math.max(g.x,g.y)),s.lastScreenCoverage=Math.max(g.x,g.y,g.z),s.lastScreenspaceVolume.copy(g),s.lastScreenCoverage*=s.lastCentrality,R&&M.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const m=M.corner0,O=M.corner1,T=M.corner2,G=M.corner3;m.copy(this._tempBox.min),O.copy(this._tempBox.max),O.x=m.x,T.copy(this._tempBox.max),T.y=m.y,G.copy(this._tempBox.max);const j=(m.z+G.z)*.5;m.z=O.z=T.z=G.z=j,m.applyMatrix4(h),O.applyMatrix4(h),T.applyMatrix4(h),G.applyMatrix4(h),M.debugDrawLine(m,O,255),M.debugDrawLine(m,T,255),M.debugDrawLine(O,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const m=u[h],T=(m.densities?.[c]||m.density||1e-5)/s.lastScreenCoverage;if(c>0&&_e()&&!m.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T<o){v=h;break}}v<l&&(l=v,a=!0)}if(a?r.mesh_lod=l:r.mesh_lod=s.lastLodLevel_Mesh,R&&r.mesh_lod!=s.lastLodLevel_Mesh){const g=u?.[r.mesh_lod];g&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${g.densities?.[c].toFixed(0)}) | ${e.name}`)}if(_){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=L.max_count-1,R){const g=L.lods[L.max_count-1];R&&console.log(`First Texture LOD ${r.texture_lod} (${g.max_height}px) - ${e.name}`)}}else{const g=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const D=I/window.devicePixelRatio*b;let z=!1;for(let P=L.lods.length-1;P>=0;P--){const v=L.lods[P];if(!(f&&v.max_height>=2048)&&!(Ae()&&v.max_height>4096)&&(v.max_height>D||!z&&P===0)){if(z=!0,r.texture_lod=P,R&&r.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${w}px
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${e.name}`)}break}}}}else r.texture_lod=0}}class
|
|
8
|
-
`,
|
|
6
|
+
`,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 ut{url;key;level;index;constructor(t,e,s,o){this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o)}}class le{static addPromise=(t,e,s,o)=>{o.forEach(r=>{r.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 o=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=o,this.ready=new Promise(r=>{this._resolve=r}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 R=q("debugprogressive"),ct=q("noprogressive"),ue=Symbol("Needle:LODSManager"),ce=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),A={mesh_lod:-1,texture_lod:-1};class M{static debugDrawLine;static getObjectLODState(t){return t[ce]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const s=new M(t,{engine:"unknown",...e});return t[ue]=s,s}renderer;context;projectionScreenMatrix=new c.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 le(this.#r,{...t});this._newPromiseGroups.push(s);const o=performance.now();return s.ready.finally(()=>{const r=this._newPromiseGroups.indexOf(s);r>=0&&(this._newPromiseGroups.splice(r,1),Le()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new c.Clock;#r=0;#o=0;#i=0;#s=0;_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;se(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,R&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,o),e.onAfterRender(s,o,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.renderer.renderLists.get(t,0).opaque;let i=!0;if(r.length===1){const l=r[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(ct||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,R&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,R&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const a of o){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")){R&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(R==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const d=Math.random()*16777215,x=new c.MeshStandardMaterial({color:d});a.object.material=x}const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}const i=s.transparent;for(const a of i){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,r)}}updateLODs(t,e,s,o){s.userData||(s.userData={});let r=s[ce];if(r||(r=new dt,s[ce]=r),r.frames++<2)return;for(const l of U)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;i>=0?(A.mesh_lod=i,A.texture_lod=i):(this.calculateLodLevel(e,s,r,o,A),A.mesh_lod=Math.round(A.mesh_lod),A.texture_lod=Math.round(A.texture_lod)),A.mesh_lod>=0&&this.loadProgressiveMeshes(s,A.mesh_lod),s.material&&A.texture_lod>=0&&this.loadProgressiveTextures(s.material,A.texture_lod,i),g&&s.material&&!s.isGizmo&&Re(s.material);for(const l of U)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,A);r.lastLodLevel_Mesh=A.mesh_lod,r.lastLodLevel_Texture=A.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const r of t)this.loadProgressiveTextures(r,e);return}let o=!1;if((t[W]===void 0||e<t[W])&&(o=!0),s!==void 0&&s>=0&&(o=t[W]!=s,e=s),o){t[W]=e;const r=y.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.addPromise("texture",t,r,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(s=t[W]!=o,e=o),s){t[W]=e;const r=t.geometry,i=y.assignMeshLOD(t,e).then(l=>(l&&t[W]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,i=(s.y+o.y)*.5;return this._tempPtInside.set(r,i,s.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,o,r){if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let l=10+1,a=!1;if(R&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=y.getMeshLODExtension(e.geometry)?.lods,d=y.getPrimitiveIndex(e.geometry),x=u&&u.length>0,_=y.getMaterialMinMaxLODsCount(e.material),L=_.min_count!==1/0&&_.min_count>=0&&_.max_count>=0;if(!x&&!L){r.mesh_lod=0,r.texture_lod=0;return}x||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[M.$skinnedMeshBoundsOffset]){const b=M.skinnedMeshBoundsFrameOffsetCounter++;f[M.$skinnedMeshBoundsOffset]=b}const p=f[M.$skinnedMeshBoundsOffset];if((s.frames+p)%this.skinnedMeshAutoUpdateBoundsInterval===0){const b=E(f),$=f.geometry;b&&(f.geometry=b),f.computeBoundingBox(),f.geometry=$}}D=f.boundingBox}if(D){const f=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&M.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const h=this._tempBox.min,m=this._tempBox.max;let S=h.x,T=h.y,G=m.x,j=m.y;const Q=2,re=1.5,J=(h.x+m.x)*.5,Z=(h.y+m.y)*.5;S=(S-J)*Q+J,T=(T-Z)*Q+Z,G=(G-J)*Q+J,j=(j-Z)*Q+Z;const $e=S<0&&G>0?0:Math.min(Math.abs(h.x),Math.abs(m.x)),Ge=T<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(m.y)),oe=Math.max($e,Ge);s.lastCentrality=(re-oe)*(re-oe)*(re-oe)}else s.lastCentrality=1;const p=this._tempBox.getSize(this._tempBoxSize);p.multiplyScalar(.5),screen.availHeight>0&&I>0&&p.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?p.x*=t.aspect:t.isOrthographicCamera;const b=t.matrixWorldInverse,$=this._tempBox2;$.copy(D),$.applyMatrix4(e.matrixWorld),$.applyMatrix4(b);const O=$.getSize(this._tempBox2Size),z=Math.max(O.x,O.y);if(Math.max(p.x,p.y)!=0&&z!=0&&(p.z=O.z/Math.max(O.x,O.y)*Math.max(p.x,p.y)),s.lastScreenCoverage=Math.max(p.x,p.y,p.z),s.lastScreenspaceVolume.copy(p),s.lastScreenCoverage*=s.lastCentrality,R&&M.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const m=M.corner0,S=M.corner1,T=M.corner2,G=M.corner3;m.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=m.x,T.copy(this._tempBox.max),T.y=m.y,G.copy(this._tempBox.max);const j=(m.z+G.z)*.5;m.z=S.z=T.z=G.z=j,m.applyMatrix4(h),S.applyMatrix4(h),T.applyMatrix4(h),G.applyMatrix4(h),M.debugDrawLine(m,S,255),M.debugDrawLine(m,T,255),M.debugDrawLine(S,G,255),M.debugDrawLine(T,G,255)}let v=999;if(u&&s.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const m=u[h],T=(m.densities?.[d]||m.density||1e-5)/s.lastScreenCoverage;if(d>0&&Le()&&!m.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T<o){v=h;break}}v<l&&(l=v,a=!0)}if(a?r.mesh_lod=l:r.mesh_lod=s.lastLodLevel_Mesh,R&&r.mesh_lod!=s.lastLodLevel_Mesh){const p=u?.[r.mesh_lod];p&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${p.densities?.[d].toFixed(0)}) | ${e.name}`)}if(L){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=_.max_count-1,R){const p=_.lods[_.max_count-1];R&&console.log(`First Texture LOD ${r.texture_lod} (${p.max_height}px) - ${e.name}`)}}else{const p=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let b=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(b*=1.5);const O=I/window.devicePixelRatio*b;let z=!1;for(let P=_.lods.length-1;P>=0;P--){const v=_.lods[P];if(!(f&&v.max_height>=2048)&&!(Ae()&&v.max_height>4096)&&(v.max_height>O||!z&&P===0)){if(z=!0,r.texture_lod=P,R&&r.texture_lod<s.lastLodLevel_Texture){const w=v.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${w}px
|
|
7
|
+
Screensize: ${O.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${p.toFixed(1)}
|
|
8
|
+
${e.name}`)}break}}}}else r.texture_lod=0}}class dt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const ve=Symbol("NEEDLE_mesh_lod"),ee=Symbol("NEEDLE_texture_lod");let de=null;function we(){const n=ft();n&&(n.mapURLs(function(t){return Me(),t}),Me(),de?.disconnect(),de=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&ke(s)})})}),de.observe(document,{childList:!0,subtree:!0}))}function ft(){if(typeof customElements>"u")return null;const n=customElements.get("model-viewer");return n||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function Me(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{ke(t)})}const be=new WeakSet;let ht=0;function ke(n){if(!n||be.has(n))return null;be.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++ht+`
|
|
9
|
+
`,n.getAttribute("src"));let t=null,e=null,s=null;for(let o=n;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),i=r.find(u=>u.toString()=="Symbol(renderer)"),l=r.find(u=>u.toString()=="Symbol(scene)"),a=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&l!=null&&(e=n[l]),!s&&a!=null&&(s=n[a])}if(t&&e){let o=function(){if(s){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}s?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=M.get(t,{engine:"model-viewer"});return M.addPlugin(new gt),r.enable(),r.addEventListener("changed",()=>{s?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&s?.call(n)}),n.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class gt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}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[ee]==!0)return;e[ee]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let i=function(a){if(a[ee]==!0)return;a[ee]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let d=0;d<u.length;d++){const x=u[d],_=a[x];if(_?.isTexture===!0){const L=_.userData?.associations?.textures;if(L==null)continue;const I=s.parser.json.textures[L];if(!I){console.warn("Texture data not found for texture index "+L);continue}if(I?.extensions?.[F]){const D=I.extensions[F];D&&r&&y.registerTexture(r,_,D.lods.length,L,D)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)i(a);else i(l)}}tryParseMeshLOD(t,e){if(e[ve]==!0)return;e[ve]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=e.userData?.gltfExtensions?.[F];if(r&&o){const i=e.uuid;y.registerMesh(o,i,e,0,r.lods.length,r)}}}function Be(...n){let t,e,s,o;switch(n.length){case 2:[s,e]=n,o={};break;case 3:[s,e,o]=n;break;case 4:[t,e,s,o]=n;break;default:throw new Error("Invalid arguments")}se(e),me(s),ye(s,{progressive:!0,...o?.hints}),s.register(i=>new y(i));const r=M.get(e);return o?.enableLODsManager!==!1&&r.enable(),r}we();if(!tt){const n={gltfProgressive:{useNeedleProgressive:Be,LODsManager:M,configureLoader:ye,getRaycastMesh:E,useRaycastMeshes:Ie}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}exports.EXTENSION_NAME=F;exports.LODsManager=M;exports.NEEDLE_progressive=y;exports.VERSION=De;exports.addDracoAndKTX2Loaders=me;exports.configureLoader=ye;exports.createLoaders=se;exports.getRaycastMesh=E;exports.patchModelViewer=we;exports.registerRaycastMesh=Ce;exports.setDracoDecoderLocation=Se;exports.setKTX2TranscoderLocation=Te;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=Ie;
|
package/lib/extension.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
103
103
|
/** A map of key = asset uuid and value = LOD information */
|
|
104
104
|
private static readonly lodInfos;
|
|
105
105
|
/** cache of already loaded mesh lods. Uses WeakRef for single resources to allow garbage collection when unused. */
|
|
106
|
-
private static readonly
|
|
106
|
+
private static readonly cache;
|
|
107
107
|
/** this contains the geometry/textures that were originally loaded. Uses WeakRef to allow garbage collection when unused. */
|
|
108
108
|
private static readonly lowresCache;
|
|
109
109
|
/**
|