@needle-tools/gltf-progressive 1.2.5 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/examples/modelviewer-multiple.html +1 -1
- package/examples/modelviewer.html +1 -1
- package/gltf-progressive.js +1039 -0
- package/gltf-progressive.min.js +8 -0
- package/gltf-progressive.umd.cjs +8 -0
- package/{dist/lib → lib}/lods_manager.d.ts +1 -2
- package/{dist/lib → lib}/lods_manager.js +2 -10
- package/{dist/lib → lib}/version.js +1 -1
- package/package.json +59 -73
- package/dist/CHANGELOG.md +0 -139
- package/dist/README.md +0 -125
- package/dist/examples/modelviewer-multiple.html +0 -125
- package/dist/examples/modelviewer.html +0 -33
- package/dist/examples/react-three-fiber/.prettierrc +0 -10
- package/dist/examples/react-three-fiber/favicon.png +0 -0
- package/dist/examples/react-three-fiber/index.html +0 -24
- package/dist/examples/react-three-fiber/package-lock.json +0 -4023
- package/dist/examples/react-three-fiber/package.json +0 -34
- package/dist/examples/react-three-fiber/tsconfig.json +0 -22
- package/dist/examples/react-three-fiber/vite.config.js +0 -39
- package/dist/examples/threejs/index.html +0 -51
- package/dist/examples/threejs/main.js +0 -181
- package/dist/gltf-progressive.js +0 -1043
- package/dist/gltf-progressive.min.js +0 -8
- package/dist/gltf-progressive.umd.cjs +0 -8
- package/dist/package.json +0 -60
- package/tsconfig.json +0 -42
- /package/{dist/lib → lib}/extension.d.ts +0 -0
- /package/{dist/lib → lib}/extension.js +0 -0
- /package/{dist/lib → lib}/index.d.ts +0 -0
- /package/{dist/lib → lib}/index.js +0 -0
- /package/{dist/lib → lib}/loaders.d.ts +0 -0
- /package/{dist/lib → lib}/loaders.js +0 -0
- /package/{dist/lib → lib}/plugins/index.d.ts +0 -0
- /package/{dist/lib → lib}/plugins/index.js +0 -0
- /package/{dist/lib → lib}/plugins/modelviewer.d.ts +0 -0
- /package/{dist/lib → lib}/plugins/modelviewer.js +0 -0
- /package/{dist/lib → lib}/plugins/plugin.d.ts +0 -0
- /package/{dist/lib → lib}/plugins/plugin.js +0 -0
- /package/{dist/lib → lib}/utils.d.ts +0 -0
- /package/{dist/lib → lib}/utils.internal.d.ts +0 -0
- /package/{dist/lib → lib}/utils.internal.js +0 -0
- /package/{dist/lib → lib}/utils.js +0 -0
- /package/{dist/lib → lib}/version.d.ts +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var Re=Object.defineProperty,je=(t,e,r)=>e in t?Re(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e,r)=>(je(t,typeof e!="symbol"?e+"":e,r),r),Oe=(t,e,r)=>{if(!e.has(t))throw TypeError("Cannot "+r)},v=(t,e,r)=>(Oe(t,e,"read from private field"),r?r.call(t):e.get(t)),V=(t,e,r)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,r)},G=(t,e,r,n)=>(Oe(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r);import{BufferGeometry as ie,Mesh as q,Material as Ge,Texture as Q,TextureLoader as We,Matrix4 as be,Clock as Ne,MeshStandardMaterial as Fe,Sphere as $e,Box3 as Se,Vector3 as W}from"three";import{GLTFLoader as Ue}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as ze}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ve}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as qe}from"three/examples/jsm/loaders/KTX2Loader.js";const fe="";globalThis.GLTF_PROGRESSIVE_VERSION=fe,console.debug(`[gltf-progressive] version ${fe}`);let le="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",me="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(le+"draco_decoder.js",{method:"head"}).catch(t=>{le="./include/draco/",me="./include/ktx2/"});function Xe(t){le=t}function Ke(t){me=t}let K,ae,H;function pe(t){return K||(K=new Ve,K.setDecoderPath(le),K.setDecoderConfig({type:"js"})),H||(H=new qe,H.setTranscoderPath(me)),ae||(ae=ze),t?H.detectSupport(t):t!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:K,ktx2Loader:H,meshoptDecoder:ae}}function xe(t){t.dracoLoader||t.setDRACOLoader(K),t.ktx2Loader||t.setKTX2Loader(H),t.meshoptDecoder||t.setMeshoptDecoder(ae)}Z("debugprogressive");function Z(t){const e=new URL(window.location.href).searchParams.get(t);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function He(t,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||t===void 0)return e;const r=t.lastIndexOf("/");if(r>=0){const n=t.substring(0,r+1);for(;n.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return n+e}return e}let ue;function Ye(){return ue!==void 0||(ue=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),Z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ue)),ue}const ve=Symbol("needle:raycast-mesh");function ee(t){return t?.[ve]instanceof ie?t[ve]:null}function Te(t,e){if((t.type==="Mesh"||t.type==="SkinnedMesh")&&!ee(t)){const r=Qe(e);r.userData={isRaycastMesh:!0},t[ve]=r}}function Je(t=!0){if(t){if(te)return;const e=te=q.prototype.raycast;q.prototype.raycast=function(r,n){const s=this,o=ee(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),e.call(this,r,n),i&&(s.geometry=i)}}else{if(!te)return;q.prototype.raycast=te,te=null}}let te=null;function Qe(t){const e=new ie;for(const r in t.attributes)e.setAttribute(r,t.getAttribute(r));return e.setIndex(t.getIndex()),e}const F=new Array,R="NEEDLE_progressive",y=Z("debugprogressive"),ye=Symbol("needle-progressive-texture"),re=new Map,Le=new Set;if(y){let t=function(){e+=1,console.log("Toggle LOD level",e,re),re.forEach((s,o)=>{for(const i of s.keys){const l=o[i];if(l!=null){if(l.isBufferGeometry===!0){const u=S.getMeshLODInformation(l),a=u?Math.min(e,u.lods.length):0;o["DEBUG:LOD"]=e,S.assignMeshLOD(o,a),u&&(r=Math.max(r,u.lods.length-1))}else if(o.isMaterial===!0){o["DEBUG:LOD"]=e,S.assignTextureLOD(o,e);break}}}}),e>=r&&(e=-1)},e=-1,r=2,n=!1;window.addEventListener("keyup",s=>{s.key==="p"&&t(),s.key==="w"&&(n=!n,Le&&Le.forEach(o=>{o.name!="BackgroundCubeMaterial"&&o.glyphMap==null&&"wireframe"in o&&(o.wireframe=n)}))})}function Ee(t,e,r){var n;if(!y)return;re.has(t)||re.set(t,{keys:[],sourceId:r});const s=re.get(t);((n=s?.keys)==null?void 0:n.includes(e))==!1&&s.keys.push(e)}const w=class{constructor(t,e){c(this,"parser"),c(this,"url"),c(this,"_isLoadingMesh"),c(this,"loadMesh",r=>{var n,s;if(this._isLoadingMesh)return null;const o=(s=(n=this.parser.json.meshes[r])==null?void 0:n.extensions)==null?void 0:s[R];return o?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",r).then(i=>{var l;return this._isLoadingMesh=!1,i&&w.registerMesh(this.url,o.guid,i,(l=o.lods)==null?void 0:l.length,void 0,o),i})):null}),y&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return R}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const r=this,n="LODS:minmax",s=t[n];if(s!=null)return s;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const i of t)this.getMaterialMinMaxLODsCount(i,e);return t[n]=e,e}if(y==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const i=t;for(const l of Object.keys(i.uniforms)){const u=i.uniforms[l].value;u?.isTexture===!0&&o(u,e)}}else if(t.isMaterial)for(const i of Object.keys(t)){const l=t[i];l?.isTexture===!0&&o(l,e)}return t[n]=e,e;function o(i,l){const u=r.getAssignedLODInformation(i);if(u){const a=r.lodInfos.get(u.key);if(a&&a.lods){l.min_count=Math.min(l.min_count,a.lods.length),l.max_count=Math.max(l.max_count,a.lods.length);for(let g=0;g<a.lods.length;g++){const f=a.lods[g];f.width&&(l.lods[g]=l.lods[g]||{min_height:1/0,max_height:0},l.lods[g].min_height=Math.min(l.lods[g].min_height,f.height),l.lods[g].max_height=Math.max(l.lods[g].max_height,f.height))}}}}}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let n,s;if(t.isMesh?n=t.geometry:(t.isBufferGeometry||t.isTexture)&&(n=t),n&&(r=n?.userData)!=null&&r.LODS){const o=n.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e){var r;if(!t)return Promise.resolve(null);if(t instanceof q||t.isMesh===!0){const n=t.geometry,s=this.getAssignedLODInformation(n);if(!s)return Promise.resolve(null);for(const o of F)(r=o.onBeforeGetLODMesh)==null||r.call(o,t,e);return t["LOD:requested level"]=e,w.getOrLoadLOD(n,e).then(o=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(o)){const i=s.index||0;o=o[i]}o&&n!=o&&(o?.isBufferGeometry?(t.geometry=o,y&&Ee(t,"geometry",s.url)):y&&console.error("Invalid LOD geometry",o))}return o}).catch(o=>(console.error("Error loading mesh LOD",t,o),null))}else y&&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 instanceof Ge||t.isMaterial===!0){const r=t,n=[],s=new Array;if(y&&Le.add(r),r.uniforms&&r.isRawShaderMaterial||r.isShaderMaterial===!0){const o=r;for(const i of Object.keys(o.uniforms)){const l=o.uniforms[i].value;if(l?.isTexture===!0){const u=this.assignTextureLODForSlot(l,e,r,i);n.push(u),s.push(i)}}}else for(const o of Object.keys(r)){const i=r[o];if(i?.isTexture===!0){const l=this.assignTextureLODForSlot(i,e,r,o);n.push(l),s.push(o)}}return Promise.all(n).then(o=>{const i=new Array;for(let l=0;l<o.length;l++){const u=o[l],a=s[l];u&&u.isTexture===!0?i.push({material:r,slot:a,texture:u,level:e}):i.push({material:r,slot:a,texture:null,level:e})}return i})}if(t instanceof Q||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):w.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return null;if(s?.isTexture===!0){if(s!=t){if(r&&n){const o=r[n];if(o){const i=this.getAssignedLODInformation(o);if(i&&i?.level<e)return y==="verbose"&&console.warn("Assigned texture level is already higher: ",i.level,e,r,o,s),null}r[n]=s}if(y&&n&&r){const o=this.getAssignedLODInformation(t);o&&Ee(r,n,o.url)}}return s}else y=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}afterRoot(t){var e,r;return y&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((n,s)=>{var o;if(n!=null&&n.extensions){const i=n?.extensions[R];if(i){if(!i.lods){y&&console.warn("Texture has no LODs",i);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const a=this.parser.associations.get(u);a?.textures===s&&(l=!0,w.registerTexture(this.url,u,(o=i.lods)==null?void 0:o.length,s,i))}l||this.parser.getDependency("texture",s).then(u=>{var a;u&&w.registerTexture(this.url,u,(a=i.lods)==null?void 0:a.length,s,i)})}}}),(r=this.parser.json.meshes)==null||r.forEach((n,s)=>{if(n!=null&&n.extensions){const o=n?.extensions[R];if(o&&o.lods){for(const i of this.parser.associations.keys())if(i.isMesh){const l=this.parser.associations.get(i);l?.meshes===s&&w.registerMesh(this.url,o.guid,i,o.lods.length,l.primitives,o)}}}}),null}static async getOrLoadLOD(t,e){var r,n,s,o;const i=y=="verbose",l=t.userData.LODS;if(!l)return null;const u=l?.key;let a;if(t.isTexture===!0){const g=t;g.source&&g.source[ye]&&(a=g.source[ye])}if(a||(a=w.lodInfos.get(u)),a){if(e>0){let p=!1;const A=Array.isArray(a.lods);if(A&&e>=a.lods.length?p=!0:A||(p=!0),p)return this.lowresCache.get(u)}const g=Array.isArray(a.lods)?(r=a.lods[e])==null?void 0:r.path:a.lods;if(!g)return y&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const f=He(l.url,g);if(f.endsWith(".glb")||f.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const p=f+"_"+a.guid,A=this.previouslyLoaded.get(p);if(A!==void 0){i&&console.log(`LOD ${e} was already loading/loaded: ${p}`);let d=await A.catch(O=>(console.error(`Error loading LOD ${e} from ${f}
|
|
2
|
+
`,O),null)),_=!1;if(d==null||(d instanceof Q&&t instanceof Q?(n=d.image)!=null&&n.data||(s=d.source)!=null&&s.data?d=this.copySettings(t,d):(_=!0,this.previouslyLoaded.delete(p)):d instanceof ie&&t instanceof ie&&((o=d.attributes.position)!=null&&o.array||(_=!0,this.previouslyLoaded.delete(p)))),!_)return d}const D=a,L=new Promise(async(d,_)=>{const O=new Ue;xe(O),y&&(await new Promise(m=>setTimeout(m,1e3)),i&&console.warn("Start loading (delayed) "+f,D.guid));let I=f;if(D&&Array.isArray(D.lods)){const m=D.lods[e];m.hash&&(I+="?v="+m.hash)}const T=await O.loadAsync(I).catch(m=>(console.error(`Error loading LOD ${e} from ${f}
|
|
3
|
+
`,m),null));if(!T)return null;const N=T.parser;i&&console.log("Loading finished "+f,D.guid);let x=0;if(T.parser.json.textures){let m=!1;for(const h of T.parser.json.textures){if(h!=null&&h.extensions){const M=h?.extensions[R];if(M!=null&&M.guid&&M.guid===D.guid){m=!0;break}}x++}if(m){let h=await N.getDependency("texture",x);return h&&w.assignLODInformation(l.url,h,u,e,void 0,void 0),i&&console.log('change "'+t.name+'" \u2192 "'+h.name+'"',f,x,h,p),t instanceof Q&&(h=this.copySettings(t,h)),h&&(h.guid=D.guid),d(h)}else y&&console.warn("Could not find texture with guid",D.guid,T.parser.json)}if(x=0,T.parser.json.meshes){let m=!1;for(const h of T.parser.json.meshes){if(h!=null&&h.extensions){const M=h?.extensions[R];if(M!=null&&M.guid&&M.guid===D.guid){m=!0;break}}x++}if(m){const h=await N.getDependency("mesh",x),M=D;if(i&&console.log(`Loaded Mesh "${h.name}"`,f,x,h,p),h.isMesh===!0){const b=h.geometry;return w.assignLODInformation(l.url,b,u,e,void 0,M.density),d(b)}else{const b=new Array;for(let C=0;C<h.children.length;C++){const z=h.children[C];if(z.isMesh===!0){const X=z.geometry;w.assignLODInformation(l.url,X,u,e,C,M.density),b.push(X)}}return d(b)}}else y&&console.warn("Could not find mesh with guid",D.guid,T.parser.json)}return d(null)});return this.previouslyLoaded.set(p,L),await L}else if(t instanceof Q){i&&console.log("Load texture from uri: "+f);const p=await new We().loadAsync(f);return p?(p.guid=a.guid,p.flipY=!1,p.needsUpdate=!0,p.colorSpace=t.colorSpace,i&&console.log(a,p)):y&&console.warn("failed loading",f),p}}else y&&console.warn(`Can not load LOD ${e}: no LOD info found for "${u}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,r,n,s,o){if(!e)return;e.userData||(e.userData={});const i=new Ze(t,r,n,s,o);e.userData.LODS=i}static getAssignedLODInformation(t){var e;return((e=t?.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),y&&console.warn(`Copying texture settings
|
|
4
|
+
`,t.uuid,`
|
|
5
|
+
`,e.uuid),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}};let S=w;c(S,"registerTexture",(t,e,r,n,s)=>{if(y&&console.log("> Progressive: register texture",n,e.name,e.uuid,e,s),!e){y&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ye]=s);const o=s.guid;w.assignLODInformation(t,e,o,r,n,void 0),w.lodInfos.set(o,s),w.lowresCache.set(o,e)}),c(S,"registerMesh",(t,e,r,n,s,o)=>{var i;y&&console.log("> Progressive: register mesh",s,r.name,o,r.uuid,r);const l=r.geometry;if(!l){y&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),w.assignLODInformation(t,l,e,n,s,o.density),w.lodInfos.set(e,o);let u=w.lowresCache.get(e);u?u.push(r.geometry):u=[r.geometry],w.lowresCache.set(e,u),n>0&&!ee(r)&&Te(r,l);for(const a of F)(i=a.onRegisteredNewMesh)==null||i.call(a,r,o)}),c(S,"lodInfos",new Map),c(S,"previouslyLoaded",new Map),c(S,"lowresCache",new Map);class Ze{constructor(e,r,n,s,o){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=r,this.level=n,s!=null&&(this.index=s),o!=null&&(this.density=o)}}const P=Z("debugprogressive"),et=Z("noprogressive"),Me=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),Y=Symbol("Needle:CurrentLOD"),k={mesh_lod:-1,texture_lod:-1};var E,$,we,J,se,ce,U;const B=class{constructor(t,e){c(this,"context"),c(this,"renderer"),c(this,"projectionScreenMatrix",new be),c(this,"targetTriangleDensity",2e5),c(this,"updateInterval","auto"),V(this,E,1),c(this,"pause",!1),c(this,"manual",!1),c(this,"_lodchangedlisteners",[]),V(this,$,void 0),V(this,we,new Ne),V(this,J,0),V(this,se,0),V(this,ce,0),V(this,U,0),c(this,"_fpsBuffer",[60,60,60,60,60]),c(this,"_sphere",new $e),c(this,"_tempBox",new Se),c(this,"_tempBox2",new Se),c(this,"tempMatrix",new be),c(this,"_tempWorldPosition",new W),c(this,"_tempBoxSize",new W),c(this,"_tempBox2Size",new W),this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[De]}static addPlugin(t){F.push(t)}static removePlugin(t){const e=F.indexOf(t);e>=0&&F.splice(e,1)}static get(t,e){if(t[Me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Me];const r=new B(t,{engine:"unknown",...e});return t[Me]=r,r}get plugins(){return F}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}enable(){if(v(this,$))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;G(this,$,this.renderer.render);const e=this;pe(this.renderer),this.renderer.render=function(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,G(e,J,v(e,J)+1),G(e,se,v(e,we).getDelta()),G(e,ce,v(e,ce)+v(e,se)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/v(e,se)),G(e,U,e._fpsBuffer.reduce((i,l)=>i+l)/e._fpsBuffer.length),P&&v(e,J)%200===0&&console.log("FPS",Math.round(v(e,U)),"Interval:",v(e,E)));const o=t++;v(e,$).call(this,r,n),e.onAfterRender(r,n,o)}}disable(){v(this,$)&&(this.renderer.render=v(this,$),G(this,$,void 0))}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const n=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(n.length===1){const o=n[0].material;(o.name==="EffectMaterial"||o.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(et||(this.updateInterval==="auto"?v(this,U)<40&&v(this,E)<10?(G(this,E,v(this,E)+1),P&&console.warn("\u2193 Reducing LOD updates",v(this,E),v(this,U).toFixed(0))):v(this,U)>=60&&v(this,E)>1&&(G(this,E,v(this,E)-1),P&&console.warn("\u2191 Increasing LOD updates",v(this,E),v(this,U).toFixed(0))):G(this,E,this.updateInterval),v(this,E)>0&&v(this,J)%v(this,E)!=0))return;this.internalUpdate(t,e)}}internalUpdate(t,e){var r,n;const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const i=this.targetTriangleDensity;for(const a of o){if(a.material&&(((r=a.geometry)==null?void 0:r.type)==="BoxGeometry"||((n=a.geometry)==null?void 0:n.type)==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){P&&(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(P==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,p=new Fe({color:f});a.object.material=p}const g=a.object;(g instanceof q||g.isMesh)&&this.updateLODs(t,e,g,i)}const l=s.transparent;for(const a of l){const g=a.object;(g instanceof q||g.isMesh)&&this.updateLODs(t,e,g,i)}const u=s.transmissive;for(const a of u){const g=a.object;(g instanceof q||g.isMesh)&&this.updateLODs(t,e,g,i)}}updateLODs(t,e,r,n){var s,o;r.userData||(r.userData={});let i=r[De];if(i||(i=new tt,r[De]=i),i.frames++<2)return;for(const u of F)(s=u.onBeforeUpdateLOD)==null||s.call(u,this.renderer,t,e,r);this.calculateLodLevel(e,r,i,n,k),k.mesh_lod=Math.round(k.mesh_lod),k.texture_lod=Math.round(k.texture_lod),k.mesh_lod>=0&&this.loadProgressiveMeshes(r,k.mesh_lod);let l=k.texture_lod;if(r.material&&l>=0){const u=r["DEBUG:LOD"];u!=null&&(l=u),this.loadProgressiveTextures(r.material,l)}for(const u of F)(o=u.onAfterUpdatedLOD)==null||o.call(u,this.renderer,t,e,r,k);i.lastLodLevel_Mesh=k.mesh_lod,i.lastLodLevel_Texture=k.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}let r=!1;(t[Y]===void 0||e<t[Y])&&(r=!0),r&&(t[Y]=e,S.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(s=>s({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t[Y]!==e){t[Y]=e;const r=t.geometry;return S.assignMeshLOD(t,e).then(n=>(n&&t[Y]==e&&r!=t.geometry&&this._lodchangedlisteners.forEach(s=>s({type:"mesh",level:e,object:t})),n))}return Promise.resolve(null)}static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,o=(r.y+n.y)*.5;return this._tempPtInside.set(s,o,r.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,r,n,s){var o;if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let i=10+1,l=!1;if(P&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=S.getMeshLODInformation(e.geometry),a=u?.lods,g=a&&a.length>0,f=S.getMaterialMinMaxLODsCount(e.material),p=f?.min_count!=1/0&&f.min_count>0&&f.max_count>0;if(!g&&!p){s.mesh_lod=0,s.texture_lod=0;return}g||(l=!0,i=0);const A=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let D=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(r.frames%30===0){const d=ee(L),_=L.geometry;d&&(L.geometry=d),L.computeBoundingBox(),L.geometry=_}D=L.boundingBox}if(D&&t.isPerspectiveCamera){const L=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const x=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(x)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(D),this._tempBox.applyMatrix4(e.matrixWorld),B.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&L.fov>70){const x=this._tempBox.min,m=this._tempBox.max;let h=x.x,M=x.y,b=m.x,C=m.y;const z=2,X=1.5,oe=(x.x+m.x)*.5,ne=(x.y+m.y)*.5;h=(h-oe)*z+oe,M=(M-ne)*z+ne,b=(b-oe)*z+oe,C=(C-ne)*z+ne;const ke=h<0&&b>0?0:Math.min(Math.abs(x.x),Math.abs(m.x)),Ce=M<0&&C>0?0:Math.min(Math.abs(x.y),Math.abs(m.y)),ge=Math.max(ke,Ce);r.lastCentrality=(X-ge)*(X-ge)*(X-ge)}else r.lastCentrality=1;const d=this._tempBox.getSize(this._tempBoxSize);d.multiplyScalar(.5),screen.availHeight>0&&A>0&&d.multiplyScalar(A/screen.availHeight),d.x*=L.aspect;const _=t.matrixWorldInverse,O=this._tempBox2;O.copy(D),O.applyMatrix4(e.matrixWorld),O.applyMatrix4(_);const I=O.getSize(this._tempBox2Size),T=Math.max(I.x,I.y);if(Math.max(d.x,d.y)!=0&&T!=0&&(d.z=I.z/Math.max(I.x,I.y)*Math.max(d.x,d.y)),r.lastScreenCoverage=Math.max(d.x,d.y,d.z),r.lastScreenspaceVolume.copy(d),r.lastScreenCoverage*=r.lastCentrality,P&&B.debugDrawLine){const x=this.tempMatrix.copy(this.projectionScreenMatrix);x.invert();const m=B.corner0,h=B.corner1,M=B.corner2,b=B.corner3;m.copy(this._tempBox.min),h.copy(this._tempBox.max),h.x=m.x,M.copy(this._tempBox.max),M.y=m.y,b.copy(this._tempBox.max);const C=(m.z+b.z)*.5;m.z=h.z=M.z=b.z=C,m.applyMatrix4(x),h.applyMatrix4(x),M.applyMatrix4(x),b.applyMatrix4(x),B.debugDrawLine(m,h,255),B.debugDrawLine(m,M,255),B.debugDrawLine(h,b,255),B.debugDrawLine(M,b,255)}let N=999;if(a&&r.lastScreenCoverage>0){for(let x=0;x<a.length;x++)if(a[x].density/r.lastScreenCoverage<n){N=x;break}}N<i&&(i=N,l=!0)}if(l?s.mesh_lod=i:s.mesh_lod=r.lastLodLevel_Mesh,P&&s.mesh_lod!=r.lastLodLevel_Mesh){const L=a?.[s.mesh_lod];L&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (${L.density.toFixed(0)}) - ${e.name}`)}if(p){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=f.max_count-1,P){const d=f.lods[f.max_count-1];P&&console.log(`First Texture LOD ${s.texture_lod} (${d.max_height}px) - ${e.name}`)}}else{const d=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let _=r.lastScreenCoverage*2;((o=this.context)==null?void 0:o.engine)==="model-viewer"&&(_*=2);const O=A/window.devicePixelRatio*_;for(let I=f.lods.length-1;I>=0;I--){let T=f.lods[I];if(!(L&&T.max_height>=2048)&&!(Ye()&&T.max_height>4096)&&T.max_height>O){if(s.texture_lod=I,s.texture_lod<r.lastLodLevel_Texture){const N=T.max_height;P&&console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${N}px
|
|
6
|
+
Screensize: ${O.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${d.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else s.texture_lod=0}};let j=B;E=new WeakMap,$=new WeakMap,we=new WeakMap,J=new WeakMap,se=new WeakMap,ce=new WeakMap,U=new WeakMap,c(j,"debugDrawLine"),c(j,"corner0",new W),c(j,"corner1",new W),c(j,"corner2",new W),c(j,"corner3",new W),c(j,"_tempPtInside",new W);class tt{constructor(){c(this,"frames",0),c(this,"lastLodLevel_Mesh",-1),c(this,"lastLodLevel_Texture",-1),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new W),c(this,"lastCentrality",0)}}const Ae=Symbol("NEEDLE_mesh_lod"),de=Symbol("NEEDLE_texture_lod");let he=null;function _e(){const t=rt();t&&(t.mapURLs(function(e){return Ie(),e}),Ie(),he?.disconnect(),he=new MutationObserver(e=>{e.forEach(r=>{r.addedNodes.forEach(n=>{n instanceof HTMLElement&&n.tagName.toLowerCase()==="model-viewer"&&Pe(n)})})}),he.observe(document,{childList:!0,subtree:!0}))}function rt(){return customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),_e()}),null)}function Ie(){document.querySelectorAll("model-viewer").forEach(t=>{Pe(t)})}const Be=new WeakSet;let st=0;function Pe(t){if(!t||Be.has(t))return null;Be.add(t),console.debug("[gltf-progressive] found new model-viewer..."+ ++st+`
|
|
8
|
+
`,t.getAttribute("src"));let e=null,r=null,n=null;for(let s=t;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(a=>a.toString()=="Symbol(renderer)"),l=o.find(a=>a.toString()=="Symbol(scene)"),u=o.find(a=>a.toString()=="Symbol(needsRender)");!e&&i!=null&&(e=t[i].threeRenderer),!r&&l!=null&&(r=t[l]),!n&&u!=null&&(n=t[u])}if(e&&r){let s=function(){if(n){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}n?.call(t)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=j.get(e,{engine:"model-viewer"});return j.addPlugin(new ot),o.enable(),o.addEventListener("changed",()=>{n?.call(t)}),t.addEventListener("model-visibility",i=>{i.detail.visible&&n?.call(t)}),t.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class ot{constructor(){c(this,"_didWarnAboutMissingUrl",!1)}onBeforeUpdateLOD(e,r,n,s){this.tryParseMeshLOD(r,s),this.tryParseTextureLOD(r,s)}getUrl(e){if(!e)return null;let r=e.getAttribute("src");return r||(r=e.src),r||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",e),this._didWarnAboutMissingUrl=!0),r}tryGetCurrentGLTF(e){return e._currentGLTF}tryGetCurrentModelViewer(e){return e.element}tryParseTextureLOD(e,r){if(r[de]==!0)return;r[de]=!0;const n=this.tryGetCurrentGLTF(e),s=this.tryGetCurrentModelViewer(e),o=this.getUrl(s);if(o&&n&&r.material){let i=function(u){var a,g,f;if(u[de]==!0)return;u[de]=!0,u.userData&&(u.userData.LOD=-1);const p=Object.keys(u);for(let A=0;A<p.length;A++){const D=p[A],L=u[D];if(L?.isTexture===!0){const d=(g=(a=L.userData)==null?void 0:a.associations)==null?void 0:g.textures;if(d==null)continue;const _=n.parser.json.textures[d];if(!_){console.warn("Texture data not found for texture index "+d);continue}if((f=_?.extensions)!=null&&f[R]){const O=_.extensions[R];O&&o&&S.registerTexture(o,L,O.lods.length,d,O)}}}};const l=r.material;if(Array.isArray(l))for(const u of l)i(u);else i(l)}}tryParseMeshLOD(e,r){var n,s;if(r[Ae]==!0)return;r[Ae]=!0;const o=this.tryGetCurrentModelViewer(e),i=this.getUrl(o);if(!i)return;const l=(s=(n=r.userData)==null?void 0:n.gltfExtensions)==null?void 0:s[R];if(l&&i){const u=r.uuid;S.registerMesh(i,u,r,0,l.lods.length,l)}}}function nt(t,e,r,n){pe(e),xe(r),r.register(o=>new S(o,t));const s=j.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}_e();export{R as EXTENSION_NAME,j as LODsManager,S as NEEDLE_progressive,fe as VERSION,xe as addDracoAndKTX2Loaders,pe as createLoaders,ee as getRaycastMesh,_e as patchModelViewer,Te as registerRaycastMesh,Xe as setDracoDecoderLocation,Ke as setKTX2TranscoderLocation,nt as useNeedleProgressive,Je as useRaycastMeshes};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";var Re=Object.defineProperty;var ke=(a,t,e)=>t in a?Re(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var d=(a,t,e)=>(ke(a,typeof t!="symbol"?t+"":t,e),e),Se=(a,t,e)=>{if(!t.has(a))throw TypeError("Cannot "+e)};var L=(a,t,e)=>(Se(a,t,"read from private field"),e?e.call(a):t.get(a)),K=(a,t,e)=>{if(t.has(a))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(a):t.set(a,e)},N=(a,t,e,s)=>(Se(a,t,"write to private field"),s?s.call(a,e):t.set(a,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("three"),Ge=require("three/examples/jsm/loaders/GLTFLoader.js"),Ve=require("three/examples/jsm/libs/meshopt_decoder.module.js"),Fe=require("three/examples/jsm/loaders/DRACOLoader.js"),$e=require("three/examples/jsm/loaders/KTX2Loader.js"),De="";globalThis.GLTF_PROGRESSIVE_VERSION=De;console.debug(`[gltf-progressive] version ${De}`);let fe="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",we="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";fetch(fe+"draco_decoder.js",{method:"head"}).catch(a=>{fe="./include/draco/",we="./include/ktx2/"});function Ne(a){fe=a}function Ue(a){we=a}let J,ue,Q;function ve(a){return J||(J=new Fe.DRACOLoader,J.setDecoderPath(fe),J.setDecoderConfig({type:"js"})),Q||(Q=new $e.KTX2Loader,Q.setTranscoderPath(we)),ue||(ue=Ve.MeshoptDecoder),a?Q.detectSupport(a):a!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:J,ktx2Loader:Q,meshoptDecoder:ue}}function _e(a){a.dracoLoader||a.setDRACOLoader(J),a.ktx2Loader||a.setKTX2Loader(Q),a.meshoptDecoder||a.setMeshoptDecoder(ue)}ie("debugprogressive");function ie(a){const e=new URL(window.location.href).searchParams.get(a);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function ze(a,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||a===void 0)return t;const e=a.lastIndexOf("/");if(e>=0){const s=a.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}let ee;function We(){return ee!==void 0||(ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),ie("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee)),ee}const xe=Symbol("needle:raycast-mesh");function oe(a){return(a==null?void 0:a[xe])instanceof p.BufferGeometry?a[xe]:null}function Pe(a,t){if((a.type==="Mesh"||a.type==="SkinnedMesh")&&!oe(a)){const s=Xe(t);s.userData={isRaycastMesh:!0},a[xe]=s}}function qe(a=!0){if(a){if(te)return;const t=te=p.Mesh.prototype.raycast;p.Mesh.prototype.raycast=function(e,s){const o=this,r=oe(o);let i;r&&o.isMesh&&(i=o.geometry,o.geometry=r),t.call(this,e,s),i&&(o.geometry=i)}}else{if(!te)return;p.Mesh.prototype.raycast=te,te=null}}let te=null;function Xe(a){const t=new p.BufferGeometry;for(const e in a.attributes)t.setAttribute(e,a.getAttribute(e));return t.setIndex(a.getIndex()),t}const Y=new Array,U="NEEDLE_progressive",x=ie("debugprogressive"),ye=Symbol("needle-progressive-texture"),re=new Map,Me=new Set;if(x){let a=function(){t+=1,console.log("Toggle LOD level",t,re),re.forEach((o,r)=>{for(const i of o.keys){const n=r[i];if(n!=null){if(n.isBufferGeometry===!0){const l=O.getMeshLODInformation(n),u=l?Math.min(t,l.lods.length):0;r["DEBUG:LOD"]=t,O.assignMeshLOD(r,u),l&&(e=Math.max(e,l.lods.length-1))}else if(r.isMaterial===!0){r["DEBUG:LOD"]=t,O.assignTextureLOD(r,t);break}}}}),t>=e&&(t=-1)},t=-1,e=2,s=!1;window.addEventListener("keyup",o=>{o.key==="p"&&a(),o.key==="w"&&(s=!s,Me&&Me.forEach(r=>{r.name!="BackgroundCubeMaterial"&&r.glyphMap==null&&"wireframe"in r&&(r.wireframe=s)}))})}function be(a,t,e){var o;if(!x)return;re.has(a)||re.set(a,{keys:[],sourceId:e});const s=re.get(a);((o=s==null?void 0:s.keys)==null?void 0:o.includes(t))==!1&&s.keys.push(t)}const _=class{constructor(t,e){d(this,"parser");d(this,"url");d(this,"_isLoadingMesh");d(this,"loadMesh",t=>{var s,o;if(this._isLoadingMesh)return null;const e=(o=(s=this.parser.json.meshes[t])==null?void 0:s.extensions)==null?void 0:o[U];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>{var i;return this._isLoadingMesh=!1,r&&_.registerMesh(this.url,e.guid,r,(i=e.lods)==null?void 0:i.length,void 0,e),r})):null});x&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}get name(){return U}static getMeshLODInformation(t){const e=this.getAssignedLODInformation(t);return e!=null&&e.key?this.lodInfos.get(e.key):null}static getMaterialMinMaxLODsCount(t,e){const s=this,o="LODS:minmax",r=t[o];if(r!=null)return r;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const n of t)this.getMaterialMinMaxLODsCount(n,e);return t[o]=e,e}if(x==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const n=t;for(const l of Object.keys(n.uniforms)){const u=n.uniforms[l].value;(u==null?void 0:u.isTexture)===!0&&i(u,e)}}else if(t.isMaterial)for(const n of Object.keys(t)){const l=t[n];(l==null?void 0:l.isTexture)===!0&&i(l,e)}return t[o]=e,e;function i(n,l){const u=s.getAssignedLODInformation(n);if(u){const c=s.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let g=0;g<c.lods.length;g++){const y=c.lods[g];y.width&&(l.lods[g]=l.lods[g]||{min_height:1/0,max_height:0},l.lods[g].min_height=Math.min(l.lods[g].min_height,y.height),l.lods[g].max_height=Math.max(l.lods[g].max_height,y.height))}}}}}static hasLODLevelAvailable(t,e){var r;if(Array.isArray(t)){for(const i of t)if(this.hasLODLevelAvailable(i,e))return!0;return!1}if(t.isMaterial===!0){for(const i of Object.keys(t)){const n=t[i];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const i of t.children)if(i.isMesh===!0&&this.hasLODLevelAvailable(i,e))return!0}let s,o;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&(r=s==null?void 0:s.userData)!=null&&r.LODS){const i=s.userData.LODS;if(o=this.lodInfos.get(i.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){var s;if(!t)return Promise.resolve(null);if(t instanceof p.Mesh||t.isMesh===!0){const o=t.geometry,r=this.getAssignedLODInformation(o);if(!r)return Promise.resolve(null);for(const i of Y)(s=i.onBeforeGetLODMesh)==null||s.call(i,t,e);return t["LOD:requested level"]=e,_.getOrLoadLOD(o,e).then(i=>{if(t["LOD:requested level"]===e){if(delete t["LOD:requested level"],Array.isArray(i)){const n=r.index||0;i=i[n]}i&&o!=i&&((i==null?void 0:i.isBufferGeometry)?(t.geometry=i,x&&be(t,"geometry",r.url)):x&&console.error("Invalid LOD geometry",i))}return i}).catch(i=>(console.error("Error loading mesh LOD",t,i),null))}else x&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0){if(!t)return Promise.resolve(null);if(t instanceof p.Material||t.isMaterial===!0){const s=t,o=[],r=new Array;if(x&&Me.add(s),s.uniforms&&s.isRawShaderMaterial||s.isShaderMaterial===!0){const i=s;for(const n of Object.keys(i.uniforms)){const l=i.uniforms[n].value;if((l==null?void 0:l.isTexture)===!0){const u=this.assignTextureLODForSlot(l,e,s,n);o.push(u),r.push(n)}}}else for(const i of Object.keys(s)){const n=s[i];if((n==null?void 0:n.isTexture)===!0){const l=this.assignTextureLODForSlot(n,e,s,i);o.push(l),r.push(i)}}return Promise.all(o).then(i=>{const n=new Array;for(let l=0;l<i.length;l++){const u=i[l],c=r[l];u&&u.isTexture===!0?n.push({material:s,slot:c,texture:u,level:e}):n.push({material:s,slot:c,texture:null,level:e})}return n})}if(t instanceof p.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,s,o){return(t==null?void 0:t.isTexture)!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):_.getOrLoadLOD(t,e).then(r=>{if(Array.isArray(r))return null;if((r==null?void 0:r.isTexture)===!0){if(r!=t){if(s&&o){const i=s[o];if(i){const n=this.getAssignedLODInformation(i);if(n&&(n==null?void 0:n.level)<e)return x==="verbose"&&console.warn("Assigned texture level is already higher: ",n.level,e,s,i,r),null}s[o]=r}if(x&&o&&s){const i=this.getAssignedLODInformation(t);i&&be(s,o,i.url)}}return r}else x=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(r=>(console.error("Error loading LOD",t,r),null))}afterRoot(t){var e,s;return x&&console.log("AFTER",this.url,t),(e=this.parser.json.textures)==null||e.forEach((o,r)=>{var i;if(o!=null&&o.extensions){const n=o==null?void 0:o.extensions[U];if(n){if(!n.lods){x&&console.warn("Texture has no LODs",n);return}let l=!1;for(const u of this.parser.associations.keys())if(u.isTexture===!0){const c=this.parser.associations.get(u);(c==null?void 0:c.textures)===r&&(l=!0,_.registerTexture(this.url,u,(i=n.lods)==null?void 0:i.length,r,n))}l||this.parser.getDependency("texture",r).then(u=>{var c;u&&_.registerTexture(this.url,u,(c=n.lods)==null?void 0:c.length,r,n)})}}}),(s=this.parser.json.meshes)==null||s.forEach((o,r)=>{if(o!=null&&o.extensions){const i=o==null?void 0:o.extensions[U];if(i&&i.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const l=this.parser.associations.get(n);(l==null?void 0:l.meshes)===r&&_.registerMesh(this.url,i.guid,n,i.lods.length,l.primitives,i)}}}}),null}static async getOrLoadLOD(t,e){var n,l,u,c;const s=x=="verbose",o=t.userData.LODS;if(!o)return null;const r=o==null?void 0:o.key;let i;if(t.isTexture===!0){const g=t;g.source&&g.source[ye]&&(i=g.source[ye])}if(i||(i=_.lodInfos.get(r)),i){if(e>0){let D=!1;const v=Array.isArray(i.lods);if(v&&e>=i.lods.length?D=!0:v||(D=!0),D)return this.lowresCache.get(r)}const g=Array.isArray(i.lods)?(n=i.lods[e])==null?void 0:n.path:i.lods;if(!g)return x&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const y=ze(o.url,g);if(y.endsWith(".glb")||y.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const D=y+"_"+i.guid,v=this.previouslyLoaded.get(D);if(v!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${D}`);let h=await v.catch($=>(console.error(`Error loading LOD ${e} from ${y}
|
|
2
|
+
`,$),null)),R=!1;if(h==null||(h instanceof p.Texture&&t instanceof p.Texture?(l=h.image)!=null&&l.data||(u=h.source)!=null&&u.data?h=this.copySettings(t,h):(R=!0,this.previouslyLoaded.delete(D)):h instanceof p.BufferGeometry&&t instanceof p.BufferGeometry&&((c=h.attributes.position)!=null&&c.array||(R=!0,this.previouslyLoaded.delete(D)))),!R)return h}const M=i,F=new Promise(async(h,R)=>{const $=new Ge.GLTFLoader;_e($),x&&(await new Promise(E=>setTimeout(E,1e3)),s&&console.warn("Start loading (delayed) "+y,M.guid));let k=y;if(M&&Array.isArray(M.lods)){const E=M.lods[e];E.hash&&(k+="?v="+E.hash)}const b=await $.loadAsync(k).catch(E=>(console.error(`Error loading LOD ${e} from ${y}
|
|
3
|
+
`,E),null));if(!b)return null;const q=b.parser;s&&console.log("Loading finished "+y,M.guid);let A=0;if(b.parser.json.textures){let E=!1;for(const f of b.parser.json.textures){if(f!=null&&f.extensions){const m=f==null?void 0:f.extensions[U];if(m!=null&&m.guid&&m.guid===M.guid){E=!0;break}}A++}if(E){let f=await q.getDependency("texture",A);return f&&_.assignLODInformation(o.url,f,r,e,void 0,void 0),s&&console.log('change "'+t.name+'" → "'+f.name+'"',y,A,f,D),t instanceof p.Texture&&(f=this.copySettings(t,f)),f&&(f.guid=M.guid),h(f)}else x&&console.warn("Could not find texture with guid",M.guid,b.parser.json)}if(A=0,b.parser.json.meshes){let E=!1;for(const f of b.parser.json.meshes){if(f!=null&&f.extensions){const m=f==null?void 0:f.extensions[U];if(m!=null&&m.guid&&m.guid===M.guid){E=!0;break}}A++}if(E){const f=await q.getDependency("mesh",A),m=M;if(s&&console.log(`Loaded Mesh "${f.name}"`,y,A,f,D),f.isMesh===!0){const S=f.geometry;return _.assignLODInformation(o.url,S,r,e,void 0,m.density),h(S)}else{const S=new Array;for(let T=0;T<f.children.length;T++){const P=f.children[T];if(P.isMesh===!0){const X=P.geometry;_.assignLODInformation(o.url,X,r,e,T,m.density),S.push(X)}}return h(S)}}else x&&console.warn("Could not find mesh with guid",M.guid,b.parser.json)}return h(null)});return this.previouslyLoaded.set(D,F),await F}else if(t instanceof p.Texture){s&&console.log("Load texture from uri: "+y);const v=await new p.TextureLoader().loadAsync(y);return v?(v.guid=i.guid,v.flipY=!1,v.needsUpdate=!0,v.colorSpace=t.colorSpace,s&&console.log(i,v)):x&&console.warn("failed loading",y),v}}else x&&console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`,t.type);return null}static assignLODInformation(t,e,s,o,r,i){if(!e)return;e.userData||(e.userData={});const n=new Ke(t,s,o,r,i);e.userData.LODS=n}static getAssignedLODInformation(t){var e;return((e=t==null?void 0:t.userData)==null?void 0:e.LODS)||null}static copySettings(t,e){return e=e.clone(),x&&console.warn(`Copying texture settings
|
|
4
|
+
`,t.uuid,`
|
|
5
|
+
`,e.uuid),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}};let O=_;d(O,"registerTexture",(t,e,s,o,r)=>{if(x&&console.log("> Progressive: register texture",o,e.name,e.uuid,e,r),!e){x&&console.error("gltf-progressive: Register texture without texture");return}e.source&&(e.source[ye]=r);const i=r.guid;_.assignLODInformation(t,e,i,s,o,void 0),_.lodInfos.set(i,r),_.lowresCache.set(i,e)}),d(O,"registerMesh",(t,e,s,o,r,i)=>{var u;x&&console.log("> Progressive: register mesh",r,s.name,i,s.uuid,s);const n=s.geometry;if(!n){x&&console.warn("gltf-progressive: Register mesh without geometry");return}n.userData||(n.userData={}),_.assignLODInformation(t,n,e,o,r,i.density),_.lodInfos.set(e,i);let l=_.lowresCache.get(e);l?l.push(s.geometry):l=[s.geometry],_.lowresCache.set(e,l),o>0&&!oe(s)&&Pe(s,n);for(const c of Y)(u=c.onRegisteredNewMesh)==null||u.call(c,s,i)}),d(O,"lodInfos",new Map),d(O,"previouslyLoaded",new Map),d(O,"lowresCache",new Map);class Ke{constructor(t,e,s,o,r){d(this,"url");d(this,"key");d(this,"level");d(this,"index");d(this,"density");this.url=t,this.key=e,this.level=s,o!=null&&(this.index=o),r!=null&&(this.density=r)}}const G=ie("debugprogressive"),Ye=ie("noprogressive"),me=Symbol("Needle:LODSManager"),Le=Symbol("Needle:LODState"),H=Symbol("Needle:CurrentLOD"),V={mesh_lod:-1,texture_lod:-1};var B,z,de,Z,j,he,W;const C=class{constructor(t,e){d(this,"context");d(this,"renderer");d(this,"projectionScreenMatrix",new p.Matrix4);d(this,"targetTriangleDensity",2e5);d(this,"updateInterval","auto");K(this,B,1);d(this,"pause",!1);d(this,"manual",!1);d(this,"_lodchangedlisteners",[]);K(this,z,void 0);K(this,de,new p.Clock);K(this,Z,0);K(this,j,0);K(this,he,0);K(this,W,0);d(this,"_fpsBuffer",[60,60,60,60,60]);d(this,"_sphere",new p.Sphere);d(this,"_tempBox",new p.Box3);d(this,"_tempBox2",new p.Box3);d(this,"tempMatrix",new p.Matrix4);d(this,"_tempWorldPosition",new p.Vector3);d(this,"_tempBoxSize",new p.Vector3);d(this,"_tempBox2Size",new p.Vector3);this.renderer=t,this.context={...e}}static getObjectLODState(t){return t[Le]}static addPlugin(t){Y.push(t)}static removePlugin(t){const e=Y.indexOf(t);e>=0&&Y.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 C(t,{engine:"unknown",...e});return t[me]=s,s}get plugins(){return Y}addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}enable(){if(L(this,z))return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;N(this,z,this.renderer.render);const e=this;ve(this.renderer),this.renderer.render=function(s,o){const r=e.renderer.getRenderTarget();(r==null||"isXRRenderTarget"in r&&r.isXRRenderTarget)&&(t=0,N(e,Z,L(e,Z)+1),N(e,j,L(e,de).getDelta()),N(e,he,L(e,he)+L(e,j)),e._fpsBuffer.shift(),e._fpsBuffer.push(1/L(e,j)),N(e,W,e._fpsBuffer.reduce((n,l)=>n+l)/e._fpsBuffer.length),G&&L(e,Z)%200===0&&console.log("FPS",Math.round(L(e,W)),"Interval:",L(e,B)));const i=t++;L(e,z).call(this,s,o),e.onAfterRender(s,o,i)}}disable(){L(this,z)&&(this.renderer.render=L(this,z),N(this,z,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 n=r[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Ye||(this.updateInterval==="auto"?L(this,W)<40&&L(this,B)<10?(N(this,B,L(this,B)+1),G&&console.warn("↓ Reducing LOD updates",L(this,B),L(this,W).toFixed(0))):L(this,W)>=60&&L(this,B)>1&&(N(this,B,L(this,B)-1),G&&console.warn("↑ Increasing LOD updates",L(this,B),L(this,W).toFixed(0))):N(this,B,this.updateInterval),L(this,B)>0&&L(this,Z)%L(this,B)!=0))return;this.internalUpdate(t,e)}}internalUpdate(t,e){var l,u;const s=this.renderer.renderLists.get(t,0),o=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const r=this.targetTriangleDensity;for(const c of o){if(c.material&&(((l=c.geometry)==null?void 0:l.type)==="BoxGeometry"||((u=c.geometry)==null?void 0:u.type)==="BufferGeometry")&&(c.material.name==="SphericalGaussianBlur"||c.material.name=="BackgroundCubeMaterial"||c.material.name==="CubemapFromEquirect"||c.material.name==="EquirectangularToCubeUV")){G&&(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(c.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",c,c.material.name,c.material.type)));continue}switch(c.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(G==="color"&&c.material&&!c.object.progressive_debug_color){c.object.progressive_debug_color=!0;const y=Math.random()*16777215,D=new p.MeshStandardMaterial({color:y});c.object.material=D}const g=c.object;(g instanceof p.Mesh||g.isMesh)&&this.updateLODs(t,e,g,r)}const i=s.transparent;for(const c of i){const g=c.object;(g instanceof p.Mesh||g.isMesh)&&this.updateLODs(t,e,g,r)}const n=s.transmissive;for(const c of n){const g=c.object;(g instanceof p.Mesh||g.isMesh)&&this.updateLODs(t,e,g,r)}}updateLODs(t,e,s,o){var n,l;s.userData||(s.userData={});let r=s[Le];if(r||(r=new He,s[Le]=r),r.frames++<2)return;for(const u of Y)(n=u.onBeforeUpdateLOD)==null||n.call(u,this.renderer,t,e,s);this.calculateLodLevel(e,s,r,o,V),V.mesh_lod=Math.round(V.mesh_lod),V.texture_lod=Math.round(V.texture_lod),V.mesh_lod>=0&&this.loadProgressiveMeshes(s,V.mesh_lod);let i=V.texture_lod;if(s.material&&i>=0){const u=s["DEBUG:LOD"];u!=null&&(i=u),this.loadProgressiveTextures(s.material,i)}for(const u of Y)(l=u.onAfterUpdatedLOD)==null||l.call(u,this.renderer,t,e,s,V);r.lastLodLevel_Mesh=V.mesh_lod,r.lastLodLevel_Texture=V.texture_lod}loadProgressiveTextures(t,e){if(!t)return;if(Array.isArray(t)){for(const o of t)this.loadProgressiveTextures(o,e);return}let s=!1;(t[H]===void 0||e<t[H])&&(s=!0),s&&(t[H]=e,O.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(r=>r({type:"texture",level:e,object:t}))}))}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);if(t[H]!==e){t[H]=e;const s=t.geometry;return O.assignMeshLOD(t,e).then(o=>(o&&t[H]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(r=>r({type:"mesh",level:e,object:t})),o))}return Promise.resolve(null)}static isInside(t,e){const s=t.min,o=t.max,r=(s.x+o.x)*.5,i=(s.y+o.y)*.5;return this._tempPtInside.set(r,i,s.z).applyMatrix4(e).z<0}calculateLodLevel(t,e,s,o,r){var F;if(!e){r.mesh_lod=-1,r.texture_lod=-1;return}if(!t){r.mesh_lod=-1,r.texture_lod=-1;return}let n=10+1,l=!1;if(G&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=O.getMeshLODInformation(e.geometry),c=u==null?void 0:u.lods,g=c&&c.length>0,y=O.getMaterialMinMaxLODsCount(e.material),D=(y==null?void 0:y.min_count)!=1/0&&y.min_count>0&&y.max_count>0;if(!g&&!D){r.mesh_lod=0,r.texture_lod=0;return}g||(l=!0,n=0);const v=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let M=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const w=e;if(!w.boundingBox)w.computeBoundingBox();else if(s.frames%30===0){const h=oe(w),R=w.geometry;h&&(w.geometry=h),w.computeBoundingBox(),w.geometry=R}M=w.boundingBox}if(M&&t.isPerspectiveCamera){const w=t;if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const f=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f)){r.mesh_lod=0,r.texture_lod=0;return}}if(this._tempBox.copy(M),this._tempBox.applyMatrix4(e.matrixWorld),C.isInside(this._tempBox,this.projectionScreenMatrix)){r.mesh_lod=0,r.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&w.fov>70){const f=this._tempBox.min,m=this._tempBox.max;let S=f.x,T=f.y,P=m.x,X=m.y;const ne=2,ge=1.5,ae=(f.x+m.x)*.5,le=(f.y+m.y)*.5;S=(S-ae)*ne+ae,T=(T-le)*ne+le,P=(P-ae)*ne+ae,X=(X-le)*ne+le;const Be=S<0&&P>0?0:Math.min(Math.abs(f.x),Math.abs(m.x)),Ie=T<0&&X>0?0:Math.min(Math.abs(f.y),Math.abs(m.y)),pe=Math.max(Be,Ie);s.lastCentrality=(ge-pe)*(ge-pe)*(ge-pe)}else s.lastCentrality=1;const h=this._tempBox.getSize(this._tempBoxSize);h.multiplyScalar(.5),screen.availHeight>0&&v>0&&h.multiplyScalar(v/screen.availHeight),h.x*=w.aspect;const R=t.matrixWorldInverse,$=this._tempBox2;$.copy(M),$.applyMatrix4(e.matrixWorld),$.applyMatrix4(R);const k=$.getSize(this._tempBox2Size),b=Math.max(k.x,k.y);if(Math.max(h.x,h.y)!=0&&b!=0&&(h.z=k.z/Math.max(k.x,k.y)*Math.max(h.x,h.y)),s.lastScreenCoverage=Math.max(h.x,h.y,h.z),s.lastScreenspaceVolume.copy(h),s.lastScreenCoverage*=s.lastCentrality,G&&C.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const m=C.corner0,S=C.corner1,T=C.corner2,P=C.corner3;m.copy(this._tempBox.min),S.copy(this._tempBox.max),S.x=m.x,T.copy(this._tempBox.max),T.y=m.y,P.copy(this._tempBox.max);const X=(m.z+P.z)*.5;m.z=S.z=T.z=P.z=X,m.applyMatrix4(f),S.applyMatrix4(f),T.applyMatrix4(f),P.applyMatrix4(f),C.debugDrawLine(m,S,255),C.debugDrawLine(m,T,255),C.debugDrawLine(S,P,255),C.debugDrawLine(T,P,255)}let A=999;if(c&&s.lastScreenCoverage>0){for(let f=0;f<c.length;f++)if(c[f].density/s.lastScreenCoverage<o){A=f;break}}A<n&&(n=A,l=!0)}if(l?r.mesh_lod=n:r.mesh_lod=s.lastLodLevel_Mesh,G&&r.mesh_lod!=s.lastLodLevel_Mesh){const h=c==null?void 0:c[r.mesh_lod];h&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (${h.density.toFixed(0)}) - ${e.name}`)}if(D){const w="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(r.texture_lod=y.max_count-1,G){const h=y.lods[y.max_count-1];G&&console.log(`First Texture LOD ${r.texture_lod} (${h.max_height}px) - ${e.name}`)}}else{const h=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let R=s.lastScreenCoverage*2;((F=this.context)==null?void 0:F.engine)==="model-viewer"&&(R*=2);const k=v/window.devicePixelRatio*R;for(let b=y.lods.length-1;b>=0;b--){let q=y.lods[b];if(!(w&&q.max_height>=2048)&&!(We()&&q.max_height>4096)&&q.max_height>k){if(r.texture_lod=b,r.texture_lod<s.lastLodLevel_Texture){const A=q.max_height;G&&console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${A}px
|
|
6
|
+
Screensize: ${k.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${h.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else r.texture_lod=0}};let I=C;B=new WeakMap,z=new WeakMap,de=new WeakMap,Z=new WeakMap,j=new WeakMap,he=new WeakMap,W=new WeakMap,d(I,"debugDrawLine"),d(I,"corner0",new p.Vector3),d(I,"corner1",new p.Vector3),d(I,"corner2",new p.Vector3),d(I,"corner3",new p.Vector3),d(I,"_tempPtInside",new p.Vector3);class He{constructor(){d(this,"frames",0);d(this,"lastLodLevel_Mesh",-1);d(this,"lastLodLevel_Texture",-1);d(this,"lastScreenCoverage",0);d(this,"lastScreenspaceVolume",new p.Vector3);d(this,"lastCentrality",0)}}const Te=Symbol("NEEDLE_mesh_lod"),ce=Symbol("NEEDLE_texture_lod");let se=null;function Oe(){const a=Je();a&&(a.mapURLs(function(t){return Ae(),t}),Ae(),se==null||se.disconnect(),se=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&Ce(s)})})}),se.observe(document,{childList:!0,subtree:!0}))}function Je(){const a=customElements.get("model-viewer");return a||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Oe()}),null)}function Ae(){document.querySelectorAll("model-viewer").forEach(t=>{Ce(t)})}const Ee=new WeakSet;let Qe=0;function Ce(a){if(!a||Ee.has(a))return null;Ee.add(a),console.debug("[gltf-progressive] found new model-viewer..."+ ++Qe+`
|
|
8
|
+
`,a.getAttribute("src"));let t=null,e=null,s=null;for(let o=a;o!=null;o=Object.getPrototypeOf(o)){const r=Object.getOwnPropertySymbols(o),i=r.find(u=>u.toString()=="Symbol(renderer)"),n=r.find(u=>u.toString()=="Symbol(scene)"),l=r.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=a[i].threeRenderer),!e&&n!=null&&(e=a[n]),!s&&l!=null&&(s=a[l])}if(t&&e){let o=function(){if(s){let i=0,n=setInterval(()=>{if(i++>5){clearInterval(n);return}s==null||s.call(a)},300)}};console.debug("[gltf-progressive] setup model-viewer");const r=I.get(t,{engine:"model-viewer"});return I.addPlugin(new Ze),r.enable(),r.addEventListener("changed",()=>{s==null||s.call(a)}),a.addEventListener("model-visibility",i=>{i.detail.visible&&(s==null||s.call(a))}),a.addEventListener("load",()=>{o()}),()=>{r.disable()}}return null}class Ze{constructor(){d(this,"_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[ce]==!0)return;e[ce]=!0;const s=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),r=this.getUrl(o);if(r&&s&&e.material){let i=function(l){var c,g,y;if(l[ce]==!0)return;l[ce]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let D=0;D<u.length;D++){const v=u[D],M=l[v];if((M==null?void 0:M.isTexture)===!0){const F=(g=(c=M.userData)==null?void 0:c.associations)==null?void 0:g.textures;if(F==null)continue;const w=s.parser.json.textures[F];if(!w){console.warn("Texture data not found for texture index "+F);continue}if((y=w==null?void 0:w.extensions)!=null&&y[U]){const h=w.extensions[U];h&&r&&O.registerTexture(r,M,h.lods.length,F,h)}}}};const n=e.material;if(Array.isArray(n))for(const l of n)i(l);else i(n)}}tryParseMeshLOD(t,e){var i,n;if(e[Te]==!0)return;e[Te]=!0;const s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(!o)return;const r=(n=(i=e.userData)==null?void 0:i.gltfExtensions)==null?void 0:n[U];if(r&&o){const l=e.uuid;O.registerMesh(o,l,e,0,r.lods.length,r)}}}function je(a,t,e,s){ve(t),_e(e),e.register(r=>new O(r,a));const o=I.get(t);return(s==null?void 0:s.enableLODsManager)!==!1&&o.enable(),o}Oe();exports.EXTENSION_NAME=U;exports.LODsManager=I;exports.NEEDLE_progressive=O;exports.VERSION=De;exports.addDracoAndKTX2Loaders=_e;exports.createLoaders=ve;exports.getRaycastMesh=oe;exports.patchModelViewer=Oe;exports.registerRaycastMesh=Pe;exports.setDracoDecoderLocation=Ne;exports.setKTX2TranscoderLocation=Ue;exports.useNeedleProgressive=je;exports.useRaycastMeshes=qe;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Camera,
|
|
1
|
+
import { Camera, Material, Matrix4, Object3D, Scene, Texture, Vector3, WebGLRenderer } from "three";
|
|
2
2
|
import { NEEDLE_progressive_plugin } from "./plugins/plugin.js";
|
|
3
3
|
export type LODManagerContext = {
|
|
4
4
|
engine: "three" | "needle-engine" | "model-viewer" | "react-three-fiber" | "unknown";
|
|
@@ -59,7 +59,6 @@ export declare class LODsManager {
|
|
|
59
59
|
private readonly context;
|
|
60
60
|
readonly renderer: WebGLRenderer;
|
|
61
61
|
readonly projectionScreenMatrix: Matrix4;
|
|
62
|
-
readonly cameraFrustrum: Frustum;
|
|
63
62
|
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
64
63
|
get plugins(): NEEDLE_progressive_plugin[];
|
|
65
64
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box3, Clock,
|
|
1
|
+
import { Box3, Clock, Matrix4, Mesh, MeshStandardMaterial, Sphere, Vector3 } from "three";
|
|
2
2
|
import { NEEDLE_progressive } from "./extension.js";
|
|
3
3
|
import { createLoaders } from "./loaders.js";
|
|
4
4
|
import { getParam, isMobileDevice } from "./utils.internal.js";
|
|
@@ -75,7 +75,6 @@ export class LODsManager {
|
|
|
75
75
|
context;
|
|
76
76
|
renderer;
|
|
77
77
|
projectionScreenMatrix = new Matrix4();
|
|
78
|
-
cameraFrustrum = new Frustum();
|
|
79
78
|
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
80
79
|
get plugins() { return plugins; }
|
|
81
80
|
/**
|
|
@@ -150,7 +149,7 @@ export class LODsManager {
|
|
|
150
149
|
self._fpsBuffer.shift();
|
|
151
150
|
self._fpsBuffer.push(1 / self.#delta);
|
|
152
151
|
self.#fps = self._fpsBuffer.reduce((a, b) => a + b) / self._fpsBuffer.length;
|
|
153
|
-
if (debugProgressiveLoading && self.#frame %
|
|
152
|
+
if (debugProgressiveLoading && self.#frame % 200 === 0)
|
|
154
153
|
console.log("FPS", Math.round(self.#fps), "Interval:", self.#updateInterval);
|
|
155
154
|
}
|
|
156
155
|
const stack_level = stack++;
|
|
@@ -229,7 +228,6 @@ export class LODsManager {
|
|
|
229
228
|
const renderList = this.renderer.renderLists.get(scene, 0);
|
|
230
229
|
const opaque = renderList.opaque;
|
|
231
230
|
this.projectionScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
232
|
-
this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
|
|
233
231
|
const desiredDensity = this.targetTriangleDensity;
|
|
234
232
|
for (const entry of opaque) {
|
|
235
233
|
if (entry.material && (entry.geometry?.type === "BoxGeometry" || entry.geometry?.type === "BufferGeometry")) {
|
|
@@ -437,12 +435,6 @@ export class LODsManager {
|
|
|
437
435
|
mesh_level_calculated = true;
|
|
438
436
|
mesh_level = 0;
|
|
439
437
|
}
|
|
440
|
-
if (!this.cameraFrustrum?.intersectsObject(mesh)) {
|
|
441
|
-
// the object is not visible by the camera
|
|
442
|
-
result.mesh_lod = 100;
|
|
443
|
-
result.texture_lod = 100;
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
438
|
const canvasHeight = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
|
|
447
439
|
let boundingBox = mesh.geometry.boundingBox;
|
|
448
440
|
if (mesh.type === "SkinnedMesh") {
|
package/package.json
CHANGED
|
@@ -1,74 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@needle-tools/gltf-progressive",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "three.js support for loading glTF or GLB files that contain progressive loading data",
|
|
5
|
-
"homepage": "https://needle.tools",
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "Needle",
|
|
8
|
-
"email": "hi@needle.tools",
|
|
9
|
-
"url": "https://needle.tools/"
|
|
10
|
-
},
|
|
11
|
-
"readme": "README.md",
|
|
12
|
-
"keywords": [
|
|
13
|
-
"three.js",
|
|
14
|
-
"gltf",
|
|
15
|
-
"glb",
|
|
16
|
-
"progressive",
|
|
17
|
-
"loading",
|
|
18
|
-
"needle",
|
|
19
|
-
"engine",
|
|
20
|
-
"webgl",
|
|
21
|
-
"optimization"
|
|
22
|
-
],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"eslint-plugin-import": "^2.29.1",
|
|
61
|
-
"eslint-plugin-no-secrets": "^0.8.9",
|
|
62
|
-
"eslint-plugin-no-unsanitized": "^4.0.2",
|
|
63
|
-
"eslint-plugin-promise": "^6.1.1",
|
|
64
|
-
"eslint-plugin-xss": "^0.1.12",
|
|
65
|
-
"nodemon": "^3.1.4",
|
|
66
|
-
"npm-watch": "^0.13.0",
|
|
67
|
-
"three": ">= 0.160.0",
|
|
68
|
-
"vite": "<= 4.3.9"
|
|
69
|
-
},
|
|
70
|
-
"publishConfig": {
|
|
71
|
-
"access": "public",
|
|
72
|
-
"registry": "https://registry.npmjs.org"
|
|
73
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@needle-tools/gltf-progressive",
|
|
3
|
+
"version": "1.2.6",
|
|
4
|
+
"description": "three.js support for loading glTF or GLB files that contain progressive loading data",
|
|
5
|
+
"homepage": "https://needle.tools",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Needle",
|
|
8
|
+
"email": "hi@needle.tools",
|
|
9
|
+
"url": "https://needle.tools/"
|
|
10
|
+
},
|
|
11
|
+
"readme": "README.md",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"three.js",
|
|
14
|
+
"gltf",
|
|
15
|
+
"glb",
|
|
16
|
+
"progressive",
|
|
17
|
+
"loading",
|
|
18
|
+
"needle",
|
|
19
|
+
"engine",
|
|
20
|
+
"webgl",
|
|
21
|
+
"optimization"
|
|
22
|
+
],
|
|
23
|
+
"main": "./lib/index.js",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": "./lib/index.js",
|
|
27
|
+
"require": "./gltf-progressive.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"watch": {
|
|
31
|
+
"build:lib": {
|
|
32
|
+
"patterns": [
|
|
33
|
+
"src/**/*",
|
|
34
|
+
"examples/**/*.html"
|
|
35
|
+
],
|
|
36
|
+
"extensions": "ts,json,html",
|
|
37
|
+
"ignore": "dist"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"three": ">= 0.160.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@stylistic/eslint-plugin-ts": "^1.5.4",
|
|
45
|
+
"@types/three": "0.162.0",
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
|
47
|
+
"@typescript-eslint/parser": "^6.2.0",
|
|
48
|
+
"eslint": "^8.56.0",
|
|
49
|
+
"eslint-plugin-import": "^2.29.1",
|
|
50
|
+
"eslint-plugin-no-secrets": "^0.8.9",
|
|
51
|
+
"eslint-plugin-no-unsanitized": "^4.0.2",
|
|
52
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
53
|
+
"eslint-plugin-xss": "^0.1.12",
|
|
54
|
+
"nodemon": "^3.1.4",
|
|
55
|
+
"npm-watch": "^0.13.0",
|
|
56
|
+
"three": ">= 0.160.0",
|
|
57
|
+
"vite": "<= 4.3.9"
|
|
58
|
+
},
|
|
59
|
+
"types": "./lib/index.d.ts"
|
|
74
60
|
}
|
package/dist/CHANGELOG.md
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
All notable changes to this package will be documented in this file.
|
|
3
|
-
|
|
4
|
-
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
|
-
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
|
-
|
|
7
|
-
## [1.2.5-beta] - 2023-07-09
|
|
8
|
-
- Change: Update skinned mesh bounding box every 30 frames using the lowres mesh version
|
|
9
|
-
|
|
10
|
-
## [1.2.4-beta.1] - 2023-07-09
|
|
11
|
-
- Add: LODsManager `manual` property which can be used to manually update the LODs in the scene by calling `LODsManager.update(scene, camera)`
|
|
12
|
-
- Fix: updating LODs in WebXR
|
|
13
|
-
|
|
14
|
-
## [1.2.4-beta] - 2023-07-05
|
|
15
|
-
- Change: `createLoaders` now returns created loaders and decoders to be re-used
|
|
16
|
-
|
|
17
|
-
## [1.2.3-beta] - 2023-07-04
|
|
18
|
-
- Add: support for transmissive objects
|
|
19
|
-
|
|
20
|
-
## [1.2.3-alpha.3] - 2023-07-01
|
|
21
|
-
- Add: prevent loading highres textures when user has enabled data-save mode
|
|
22
|
-
|
|
23
|
-
## [1.2.3-alpha.2] - 2023-06-27
|
|
24
|
-
- Fix: error caused by parser associations containing `undefined` value
|
|
25
|
-
|
|
26
|
-
## [1.2.3-alpha.1] - 2023-06-25
|
|
27
|
-
- Internal: rename `setRaycastMesh` to `registerRaycastMesh`
|
|
28
|
-
|
|
29
|
-
## [1.2.3-alpha] - 2023-06-24
|
|
30
|
-
- Change: automatically change LOD update interval based on framerate
|
|
31
|
-
|
|
32
|
-
## [1.2.2-alpha.4] - 2023-06-20
|
|
33
|
-
- Add: Register version in global "GLTF_PROGRESSIVE_VERSION" variable
|
|
34
|
-
- Add: `<model-viewer>` elements added document at any time are now properly registered
|
|
35
|
-
- Fix: LOD updates for multiple `<model-viewer>` elements
|
|
36
|
-
- Fix: Initial render tick for a few frames for `<model-viewer>` to trigger LOD updates when the model-viewer element is not animated or interacted with
|
|
37
|
-
- Change: `<model-viewer>` elements will fetch a slightly higher texture LOD
|
|
38
|
-
|
|
39
|
-
## [1.2.1-alpha.4] - 2023-06-19
|
|
40
|
-
- Fix: SkinnedMesh bounds calculation
|
|
41
|
-
|
|
42
|
-
## [1.2.1-alpha.3] - 2023-06-15
|
|
43
|
-
- update the README
|
|
44
|
-
|
|
45
|
-
## [1.2.1-alpha.2] - 2023-06-15
|
|
46
|
-
- fix: Ortographic camera causing LODs being falsely updated
|
|
47
|
-
- fix: regression introduced in 1.2.1-alpha
|
|
48
|
-
- fix: error when trying to load a LOD glTF directly
|
|
49
|
-
- fix: issues caused by instanceof in local development environments
|
|
50
|
-
|
|
51
|
-
## [1.2.0-alpha.9] - 2023-06-13
|
|
52
|
-
- fix: issue where skinned mesh matrix was falsely applied to calculate screen size
|
|
53
|
-
- fix: use bounding box from SkinnedMesh object
|
|
54
|
-
|
|
55
|
-
## [1.2.0-alpha.6] - 2023-06-12
|
|
56
|
-
- fix: minor bug where opened glTF has `NEEDLE_progressive` extension but no lods array because the glTF is a LOD variant
|
|
57
|
-
|
|
58
|
-
## [1.2.0-alpha.5] - 2023-06-10
|
|
59
|
-
- fix: safeguard when `registerMesh` or `registerTexture` are being called with invalid data
|
|
60
|
-
- examples: update vanilla threejs example
|
|
61
|
-
|
|
62
|
-
## [1.2.0-alpha.4] - 2023-06-07
|
|
63
|
-
- add: `useRaycastMeshes` method:
|
|
64
|
-
```ts
|
|
65
|
-
// call to enable raycasting with low poly raycast meshes
|
|
66
|
-
// this can be done once in your project
|
|
67
|
-
useRaycastMeshes(true);
|
|
68
|
-
|
|
69
|
-
// then use the raycaster as usual
|
|
70
|
-
const raycaster = new Raycaster();
|
|
71
|
-
raycaster.setFromCamera(mouse, camera);
|
|
72
|
-
const intersects = raycaster.intersectObjects(scene.children, true);
|
|
73
|
-
|
|
74
|
-
// call to disable raycasting with low polwy meshes
|
|
75
|
-
useRaycastMeshes(false);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## [1.2.0-alpha.3] - 2023-06-06
|
|
79
|
-
- add: automatically load the highest LOD first to show a slightly better quality level as soon as possible
|
|
80
|
-
- fix: improve Texture LOD selection by taking LOD level height into account
|
|
81
|
-
- fix: correctly assign LOD level information to initially loaded texture
|
|
82
|
-
|
|
83
|
-
## [1.1.0-alpha.2] - 2023-06-05
|
|
84
|
-
- fix: register LOD information for meshes that don't have associations
|
|
85
|
-
|
|
86
|
-
## [1.1.0-alpha] - 2023-06-03
|
|
87
|
-
- add: loading of multiple texture LOD levels
|
|
88
|
-
- fix: issue where material LODs where not updated when the material was cloned
|
|
89
|
-
- change: clamp screen coverage when near plane intersects with the object bounds
|
|
90
|
-
- change: skip CubeCamera setup
|
|
91
|
-
- change: handle cases where an object has only texture LODs
|
|
92
|
-
|
|
93
|
-
## [1.0.0-alpha.19] - 2023-05-31
|
|
94
|
-
- add `LODsManager.plugins` getter
|
|
95
|
-
|
|
96
|
-
## [1.0.0-alpha.18] - 2023-05-30
|
|
97
|
-
- update README
|
|
98
|
-
|
|
99
|
-
## [1.0.0-alpha.16] - 2023-05-29
|
|
100
|
-
- fix: LODs manager now clamps to LOD 0 if the near plane is inside the bounds
|
|
101
|
-
- change: Ignore certain material in lods update loop
|
|
102
|
-
|
|
103
|
-
## [1.0.0-alpha.15] - 2023-05-25
|
|
104
|
-
- add: `getRaycastMesh` method
|
|
105
|
-
- add: LODsManager does now expose `targetTriangleDensity`. The target triangle density is the desired max amount of triangles on screen when the mesh is filling the screen.
|
|
106
|
-
- change: create LODsManager via `LODsManager.get(renderer)`
|
|
107
|
-
|
|
108
|
-
## [1.0.0-alpha.13] - 2023-05-24
|
|
109
|
-
- fix: modelviewer error when trying to access undefined texture extensions
|
|
110
|
-
|
|
111
|
-
## [1.0.0-alpha.13] - 2023-05-24
|
|
112
|
-
- add: vanilla three.js example
|
|
113
|
-
- fix: texture LODs losing filter setting
|
|
114
|
-
|
|
115
|
-
## [1.0.0-alpha.12] - 2023-05-19
|
|
116
|
-
- fix: update LODs when using postprocessing
|
|
117
|
-
|
|
118
|
-
## [1.0.0-alpha.11] - 2023-05-17
|
|
119
|
-
- add: expose `setDracoDecoderLocation` and `setKTX2TranscoderLocation`
|
|
120
|
-
- fix: allow using draco decoder and ktx2 transcoder from local filepath
|
|
121
|
-
|
|
122
|
-
## [1.0.0-alpha.10] - 2023-05-07
|
|
123
|
-
- fix: progressive assets are now only updated during the main canvas render call and not e.g. when rendering to a texture
|
|
124
|
-
|
|
125
|
-
## [1.0.0-alpha.9] - 2023-05-03
|
|
126
|
-
- fix: handle loading of ShaderMaterial for VRM progressive textures
|
|
127
|
-
|
|
128
|
-
## [1.0.0-alpha.8] - 2023-05-03
|
|
129
|
-
- fix: handle transparent materials
|
|
130
|
-
|
|
131
|
-
## [1.0.0-alpha.7] - 2023-05-01
|
|
132
|
-
- fix: Handle modelviewer `src` set as property but not as attribute
|
|
133
|
-
- change: Remove sourcemap
|
|
134
|
-
|
|
135
|
-
## [1.0.0-alpha.6] - 2023-05-01
|
|
136
|
-
- fix: LOD mesh assignment for multi-material meshes (meshes with multiple primitives)
|
|
137
|
-
|
|
138
|
-
## [1.0.0-alpha.5] - 2023-04-30
|
|
139
|
-
- initial version
|
package/dist/README.md
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# glTF progressive
|
|
2
|
-
|
|
3
|
-
Support for loading of glTF or GLB files with progressive mesh or texture data for three.js based engines.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
- Automatic loading of mesh and texture LODs.
|
|
7
|
-
- High quality LOD levels are loaded on demand based on screen density.
|
|
8
|
-
- Use low-poly LOD meshes for raycasting which allows the usage of high-poly meshes with smooth interaction
|
|
9
|
-
- Use [cloud.needle.tools](https://cloud.needle.tools) for processing glTF, GLB & VRM assets
|
|
10
|
-
|
|
11
|
-
## Examples
|
|
12
|
-
|
|
13
|
-
Examples are in the `/examples` directory. Live versions can be found in the links below.
|
|
14
|
-
|
|
15
|
-
- [Vanilla three.js](https://engine.needle.tools/demos/gltf-progressive/threejs/) - multiple models and animations
|
|
16
|
-
- [React Three Fiber](https://engine.needle.tools/demos/gltf-progressive/r3f/)
|
|
17
|
-
- \<model-viewer\>
|
|
18
|
-
- [single \<model-viewer> element](https://engine.needle.tools/demos/gltf-progressive/modelviewer)
|
|
19
|
-
- [multiple \<model-viewer> elements](https://engine.needle.tools/demos/gltf-progressive/modelviewer-multiple)
|
|
20
|
-
- [Needle Engine](https://stackblitz.com/edit/needle-engine-gltf-progressive?file=src%2Fmain.ts)
|
|
21
|
-
|
|
22
|
-
**Interactive Examples**:
|
|
23
|
-
- [Stackblitz](https://stackblitz.com/@marwie/collections/gltf-progressive)
|
|
24
|
-
- [Codesandbox](https://codesandbox.io/dashboard/sandboxes/gltf-progressive)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<br/>
|
|
28
|
-
<video width="320" controls autoplay src="https://engine.needle.tools/demos/gltf-progressive/video.mp4">
|
|
29
|
-
<source src="https://engine.needle.tools/demos/gltf-progressive/video.mp4" type="video/mp4">
|
|
30
|
-
</video>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Usage
|
|
34
|
-
|
|
35
|
-
### react three fiber
|
|
36
|
-
|
|
37
|
-
Full example in `examples/react-three-fiber`
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
function ChurchModel() {
|
|
41
|
-
const { gl } = useThree()
|
|
42
|
-
const url = 'https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb'
|
|
43
|
-
const { scene } = useGLTF(url, false, false, (loader) => {
|
|
44
|
-
useNeedleProgressive(url, gl, loader as any)
|
|
45
|
-
})
|
|
46
|
-
return <primitive object={scene} />
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### threejs (CDN, no bundler)
|
|
51
|
-
|
|
52
|
-
The full example can be found at `examples/threejs`
|
|
53
|
-
|
|
54
|
-
```html
|
|
55
|
-
<head>
|
|
56
|
-
<!-- Add the threejs import map to your HTML head section -->
|
|
57
|
-
<script type="importmap">
|
|
58
|
-
{
|
|
59
|
-
"imports": {
|
|
60
|
-
"three": "https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js",
|
|
61
|
-
"three/addons/": "https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/",
|
|
62
|
-
"three/examples/": "https://cdn.jsdelivr.net/npm/three@latest/examples/",
|
|
63
|
-
"@needle-engine/gltf-progressive": "https://www.unpkg.com/@needle-tools/gltf-progressive@latest"
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
</script>
|
|
67
|
-
</head>
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
In your script:
|
|
71
|
-
```ts
|
|
72
|
-
const gltfLoader = new GLTFLoader();
|
|
73
|
-
|
|
74
|
-
const url = "https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb";
|
|
75
|
-
|
|
76
|
-
// register the progressive loader
|
|
77
|
-
useNeedleProgressive(url, renderer, gltfLoader)
|
|
78
|
-
|
|
79
|
-
// just call the load method as usual
|
|
80
|
-
gltfLoader.load(url, gltf => {
|
|
81
|
-
console.log(gltf)
|
|
82
|
-
scene.add(gltf.scene)
|
|
83
|
-
gltf.scene.position.y += .95;
|
|
84
|
-
})
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### \<model-viewer\>
|
|
89
|
-
|
|
90
|
-
The example can be found in `examples/modelviewer.html`
|
|
91
|
-
|
|
92
|
-
```html
|
|
93
|
-
<head>
|
|
94
|
-
<!-- Include threejs import map -->
|
|
95
|
-
<script type="importmap">
|
|
96
|
-
{
|
|
97
|
-
"imports": {
|
|
98
|
-
"three": "https://unpkg.com/three/build/three.module.js",
|
|
99
|
-
"three/": "https://unpkg.com/three/"
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
</script>
|
|
103
|
-
<!-- Include gltf-progressive -->
|
|
104
|
-
<script type="module" src="https://www.unpkg.com/@needle-tools/gltf-progressive@latest"></script>
|
|
105
|
-
<!-- Include model-viewer -->
|
|
106
|
-
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
|
|
107
|
-
</head>
|
|
108
|
-
<body>
|
|
109
|
-
|
|
110
|
-
<model-viewer src="https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb" camera-controls auto-rotate></model-viewer>
|
|
111
|
-
|
|
112
|
-
</body>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Needle Engine
|
|
116
|
-
|
|
117
|
-
[Needle Engine](https://needle.tools) natively supports progressive loading of these glTF files! See [docs.needle.tools](https://docs.needle.tools) for more information.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# Contact ✒️
|
|
121
|
-
<b>[🌵 needle — tools for creators](https://needle.tools)</b> •
|
|
122
|
-
[Twitter](https://twitter.com/NeedleTools) •
|
|
123
|
-
[Discord](https://discord.needle.tools) •
|
|
124
|
-
[Forum](https://forum.needle.tools)
|
|
125
|
-
|