@needle-tools/gltf-progressive 1.0.0-alpha
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/examples/@needle-tools-gltf-progressive.min.js +3 -0
- package/examples/modelviewer.html +27 -0
- package/package.json +28 -0
- package/src/extension.ts +768 -0
- package/src/index.ts +14 -0
- package/src/loaders.ts +49 -0
- package/src/lods_manager.ts +387 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/modelviewer.ts +113 -0
- package/src/plugins/plugin.ts +21 -0
- package/src/utils.ts +41 -0
- package/tsconfig.json +33 -0
- package/vite.config.ts +69 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var oe=Object.defineProperty,ne=(r,e,t)=>e in r?oe(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,c=(r,e,t)=>(ne(r,typeof e!="symbol"?e+"":e,t),t);import{Material as Y,Texture as T,Group as ie,Mesh as I,BufferGeometry as W,RawShaderMaterial as ae,TextureLoader as le,Matrix4 as J,Frustum as ue,Sphere as ce,Box3 as Q,Vector3 as j,PerspectiveCamera as de}from"three";import{GLTFLoader as he}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as fe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as ge}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as pe}from"three/examples/jsm/loaders/KTX2Loader.js";let N="https://www.gstatic.com/draco/versioned/decoders/1.4.1/",Z="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const me=fetch(N+"draco_decoder.js",{method:"head"}).catch(r=>{console.log("Use local loaders"),N="./include/draco/",Z="./include/ktx2/"});let R,$,F;async function ye(r){await me,R||(R=new ge,R.setDecoderPath(N),R.setDecoderConfig({type:"js"})),F||(F=new pe,F.setTranscoderPath(Z)),$||($=fe),r?F.detectSupport(r):console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures will probably fail")}async function xe(r){r.setDRACOLoader(R),r.setKTX2Loader(F),r.setMeshoptDecoder($)}function q(r){let e=new URL(window.location.href).searchParams.get(r);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function ve(r,e){if(e===void 0||e.startsWith("./")||e.startsWith("http")||r===void 0)return e;const t=r.lastIndexOf("/");if(t>=0){const o=r.substring(0,t+1);for(;o.endsWith("/")&&e.startsWith("/");)e=e.substring(1);return o+e}return e}const K=new Array;function De(r){K.push(r)}const A="NEEDLE_progressive",L=q("debugprogressive"),V=Symbol("needle-progressive-texture"),G=new Map,X=new Set;if(L){let r=function(){e+=1,console.log("Toggle LOD level",e,G),G.forEach((n,s)=>{for(const i of n.keys){const a=s[i];if(a.isBufferGeometry===!0){const l=w.getMeshLODInformation(a),d=l?Math.min(e,l.lods.length):0;s["DEBUG:LOD"]=d,w.assignMeshLOD(s,d),l&&(t=Math.max(t,l.lods.length-1))}else if(s.isMaterial===!0){s["DEBUG:LOD"]=e,w.assignTextureLOD(s,e);break}}}),e>=t&&(e=-1)},e=-1,t=2,o=!1;window.addEventListener("keyup",n=>{n.key==="p"&&r(),n.key==="w"&&(o=!o,X&&X.forEach(s=>{"wireframe"in s&&(s.wireframe=o)}))})}function ee(r,e,t){var o;if(!L)return;G.has(r)||G.set(r,{keys:[],sourceId:t});const n=G.get(r);((o=n?.keys)==null?void 0:o.includes(e))==!1&&n.keys.push(e)}const O=class{constructor(r,e){c(this,"parser"),c(this,"url"),this.parser=r,this.url=e}get name(){return A}static getMeshLODInformation(r){const e=this.getAssignedLODInformation(r);return e!=null&&e.key?this.lodInfos.get(e.key):null}static hasLODLevelAvailable(r,e){var t;if(r instanceof Y){for(const s of Object.keys(r)){const i=r[s];if(i instanceof T&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(r instanceof ie){for(const s of r.children)if(s instanceof I&&this.hasLODLevelAvailable(s,e))return!0}let o,n;if(r instanceof I?o=r.geometry:r instanceof T&&(o=r),o&&(t=o?.userData)!=null&&t.LODS){const s=o.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(r,e){var t;if(!r)return Promise.resolve(null);if(r instanceof I||r.isMesh===!0){const o=r.geometry,n=this.getAssignedLODInformation(o);if(!n)return Promise.resolve(null);for(const s of K)(t=s.onBeforeGetLODMesh)==null||t.call(s,r,e);return r["LOD:requested level"]=e,O.getOrLoadLOD(o,e).then(s=>{if(r["LOD:requested level"]===e){if(delete r["LOD:requested level"],Array.isArray(s)){const i=n.index||0;s=s[i]}s&&o!=s&&s instanceof W&&(r.geometry=s,L&&ee(r,"geometry",n.url))}return s}).catch(s=>(console.error("Error loading mesh LOD",r,s),null))}else L&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",r);return Promise.resolve(null)}static assignTextureLOD(r,e=0){if(!r)return Promise.resolve(null);if(r instanceof Y||r.isMaterial===!0){const t=r,o=[],n=new Array;if(L&&X.add(t),t instanceof ae)for(const s of Object.keys(t.uniforms)){const i=t.uniforms[s].value;if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);o.push(a),n.push(s)}}else for(const s of Object.keys(t)){const i=t[s];if(i?.isTexture===!0){const a=this.assignTextureLODForSlot(i,e,t,s);o.push(a),n.push(s)}}return Promise.all(o).then(s=>{const i=new Array;for(let a=0;a<s.length;a++){const l=s[a],d=n[a];l instanceof T?i.push({material:t,slot:d,texture:l,level:e}):i.push({material:t,slot:d,texture:null,level:e})}return i})}if(r instanceof T||r.isTexture===!0){const t=r;return this.assignTextureLODForSlot(t,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(r,e,t,o){return r?.isTexture!==!0?Promise.resolve(null):O.getOrLoadLOD(r,e).then(n=>{if(Array.isArray(n))return null;if(n?.isTexture===!0){if(n!=r&&(t&&o&&(t[o]=n),L&&o&&t)){const s=this.getAssignedLODInformation(r);s&&ee(t,o,s.url)}return n}else L=="verbose"&&console.warn("No LOD found for",r,e);return null}).catch(n=>(console.error("Error loading LOD",r,n),null))}afterRoot(r){var e,t;return L&&console.log("AFTER",this.url,r),(e=this.parser.json.textures)==null||e.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[A];if(s)for(const i of this.parser.associations.keys())i instanceof T&&this.parser.associations.get(i).textures===n&&O.registerTexture(this.url,i,n,s)}}),(t=this.parser.json.meshes)==null||t.forEach((o,n)=>{if(o!=null&&o.extensions){const s=o?.extensions[A];if(s&&s.lods){for(const i of this.parser.associations.keys())if(i instanceof I){const a=this.parser.associations.get(i);a.meshes===n&&i instanceof I&&O.registerMesh(this.url,i.uuid,i,s.lods.length,a.primitives,s)}}}}),null}static async getOrLoadLOD(r,e){var t,o,n;const s=L=="verbose",i=r.userData.LODS;if(!i)return null;const a=i?.key;let l;if(r instanceof T&&r.source&&r.source[V]&&(l=r.source[V]),l||(l=O.lodInfos.get(a)),l){if(e>0){let f=!1;const v=Array.isArray(l.lods);if(v&&e>=l.lods.length?f=!0:v||(f=!0),f)return this.lowresCache.get(a)}const d=Array.isArray(l.lods)?l.lods[e].path:l.lods;if(!d)return L&&!l["missing:uri"]&&(l["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,l)),null;const m=ve(i.url,d);if(m.endsWith(".glb")||m.endsWith(".gltf")){if(!l.guid)return console.warn("missing pointer for glb/gltf texture",l),null;const f=m+"_"+l.guid,v=this.previouslyLoaded.get(f);if(v!==void 0){s&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let u=await v.catch(p=>(console.error(`Error loading LOD ${e} from ${m}
|
|
2
|
+
`,p),null)),g=!1;if(u==null||(u instanceof T&&r instanceof T?(t=u.image)!=null&&t.data||(o=u.source)!=null&&o.data?u=this.copySettings(r,u):(g=!0,this.previouslyLoaded.delete(f)):u instanceof W&&r instanceof W&&((n=u.attributes.position)!=null&&n.array||(g=!0,this.previouslyLoaded.delete(f)))),!g)return u}const _=l,S=new Promise(async(u,g)=>{const p=new he;xe(p),L&&(await new Promise(x=>setTimeout(x,1e3)),s&&console.warn("Start loading (delayed) "+m,_.guid));let b=m;if(Array.isArray(l.lods)){const x=l.lods[e];x.hash&&(b+="?v="+x.hash)}const y=await p.loadAsync(b).catch(x=>(console.error(`Error loading LOD ${e} from ${m}
|
|
3
|
+
`,x),null));if(!y)return null;const E=y.parser;s&&console.log("Loading finished "+m,_.guid);let M=0;if(y.parser.json.textures){let x=!1;for(const h of y.parser.json.textures){if(h!=null&&h.extensions){const D=h?.extensions[A];if(D!=null&&D.guid&&D.guid===_.guid){x=!0;break}}M++}if(x){let h=await E.getDependency("texture",M);return s&&console.log('change "'+r.name+'" \u2192 "'+h.name+'"',m,M,h,f),r instanceof T&&(h=this.copySettings(r,h)),h&&(h.guid=_.guid),u(h)}}if(M=0,y.parser.json.meshes){let x=!1;for(const h of y.parser.json.meshes){if(h!=null&&h.extensions){const D=h?.extensions[A];if(D!=null&&D.guid&&D.guid===_.guid){x=!0;break}}M++}if(x){const h=await E.getDependency("mesh",M),D=_;if(s&&console.log(`Loaded Mesh "${h.name}"`,m,M,h,f),h instanceof I){const k=h.geometry;return O.assignLODInformation(i.url,k,a,e,void 0,D.density),u(k)}else{const k=new Array;for(let z=0;z<h.children.length;z++){const B=h.children[z];if(B instanceof I){const H=B.geometry;O.assignLODInformation(i.url,H,a,e,z,D.density),k.push(H)}}return u(k)}}}return u(null)});return this.previouslyLoaded.set(f,S),await S}else if(r instanceof T){s&&console.log("Load texture from uri: "+m);const f=await new le().loadAsync(m);return f?(f.guid=l.guid,f.flipY=!1,f.needsUpdate=!0,f.colorSpace=r.colorSpace,s&&console.log(l,f)):L&&console.warn("failed loading",m),f}}else L&&console.warn(`Can not load LOD ${e}: no LOD info found for "${a}" ${r.name}`,r.type);return null}static assignLODInformation(r,e,t,o,n,s){if(!e)return;e.userData||(e.userData={});const i=new Le(r,t,o,n,s);e.userData.LODS=i,e.userData.LOD=o}static getAssignedLODInformation(r){var e;return((e=r?.userData)==null?void 0:e.LODS)||null}static copySettings(r,e){return this._copiedTextures.get(r)||(e=e.clone(),this._copiedTextures.set(r,e),e.offset=r.offset,e.repeat=r.repeat,e.colorSpace=r.colorSpace,e)}};let w=O;c(w,"registerTexture",(r,e,t,o)=>{L&&console.log("> Progressive: register texture",t,e.name,e.uuid,e,o),e.source&&(e.source[V]=o);const n=e.uuid;O.assignLODInformation(r,e,n,0,0,void 0),O.lodInfos.set(n,o),O.lowresCache.set(n,e)}),c(w,"registerMesh",(r,e,t,o,n,s)=>{var i;L&&console.log("> Progressive: register mesh",n,t.name,s,t.uuid,t);const a=t.geometry;a.userData||(a.userData={}),O.assignLODInformation(r,a,e,o,n,s.density),O.lodInfos.set(e,s);let l=O.lowresCache.get(e);l?l.push(t.geometry):l=[t.geometry],O.lowresCache.set(e,l);for(const d of K)(i=d.onRegisteredMesh)==null||i.call(d,t,s)}),c(w,"lodInfos",new Map),c(w,"previouslyLoaded",new Map),c(w,"lowresCache",new Map),c(w,"_copiedTextures",new Map);class Le{constructor(e,t,o,n,s){c(this,"url"),c(this,"key"),c(this,"level"),c(this,"index"),c(this,"density"),this.url=e,this.key=t,this.level=o,n!=null&&(this.index=n),s!=null&&(this.density=s)}}let re=q("debugprogressive");const Oe=q("noprogressive"),P=class{constructor(r){c(this,"renderer"),c(this,"projectionScreenMatrix",new J),c(this,"cameraFrustrum",new ue),c(this,"updateInterval",0),c(this,"pause",!1),c(this,"plugins",[]),c(this,"_originalRender"),c(this,"_sphere",new ce),c(this,"_box",new Q),c(this,"tempMatrix",new J),c(this,"_tempWorldPosition",new j),c(this,"_tempBoxSize",new j),c(this,"_tempBox2Size",new j),this.renderer=r}enable(){if(this._originalRender)return;let r=0;this._originalRender=this.renderer.render;const e=this;let t=0;ye(this.renderer),this.renderer.render=function(o,n){const s=t++,i=r++;e._originalRender.call(this,o,n),e.onUpdateLODs(o,n,i,s),r--}}disable(){this._originalRender&&(this.renderer.render=this._originalRender,this._originalRender=void 0)}onUpdateLODs(r,e,t,o){var n;if(Oe||this.pause||this.updateInterval>0&&o%this.updateInterval!=0)return;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix,this.renderer.coordinateSystem);const s=1e5,i=this.renderer.renderLists.get(r,t).opaque;for(const a of i){const l=a.object;for(const d of this.plugins)(n=d.onBeforeUpdateLOD)==null||n.call(d,this.renderer,r,e,l);(l instanceof I||l.isMesh)&&this.updateLODs(e,l,s)}}updateLODs(r,e,t){let o=e.userData.LOD_state;o||(o=new we,e.userData.LOD_state=o);let n=this.calculateLodLevel(r,e,o,t);n=Math.round(n),o.lastLodLevel=n,n>=0&&this.loadProgressiveMeshes(e,n);let s=0;if(e.material){const i=e["DEBUG:LOD"];if(i!=null&&(s=i),Array.isArray(e.material))for(const a of e.material)this.loadProgressiveTextures(a,s);else this.loadProgressiveTextures(e.material,s)}}loadProgressiveTextures(r,e){return r&&r.userData&&r.userData.LOD!==e?(r.userData.LOD=e,w.assignTextureLOD(r,e)):Promise.resolve(null)}loadProgressiveMeshes(r,e){if(e=0,!r)return Promise.resolve(null);if(r.userData||(r.userData={}),r.userData.LOD!==e){r.userData.LOD=e;const t=r.geometry;return w.assignMeshLOD(r,e).then(o=>(o&&r.userData.LOD==e&&t!=r.geometry,o))}return Promise.resolve(null)}calculateLodLevel(r,e,t,o){var n;if(!e)return-1;let s=10+1;if(r){if(re&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const i=w.getMeshLODInformation(e.geometry),a=i?.lods;if(!a||a.length<=0)return 99;if(!((n=this.cameraFrustrum)!=null&&n.intersectsObject(e)))return console.log("Mesh not visible"),99;const l=e.geometry.boundingBox;if(l&&r instanceof de){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 u=r.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(u))return 0}if(this._box.copy(l),this._box.applyMatrix4(e.matrixWorld),this._box.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&r.fov>70){const u=this._box.min,g=this._box.max;let p=u.x,b=u.y,y=g.x,E=g.y;const M=2,x=1.5,h=(u.x+g.x)*.5,D=(u.y+g.y)*.5;p=(p-h)*M+h,b=(b-D)*M+D,y=(y-h)*M+h,E=(E-D)*M+D;const k=p<0&&y>0?0:Math.min(Math.abs(u.x),Math.abs(g.x)),z=b<0&&E>0?0:Math.min(Math.abs(u.y),Math.abs(g.y)),B=Math.max(k,z);t.lastCentrality=(x-B)*(x-B)*(x-B)}else t.lastCentrality=1;const d=this._box.getSize(this._tempBoxSize);d.multiplyScalar(.5),screen.availHeight>0&&d.multiplyScalar(this.renderer.domElement.clientHeight/screen.availHeight),d.x*=r.aspect;const m=r.matrixWorldInverse,f=new Q;f.copy(l),f.applyMatrix4(e.matrixWorld),f.applyMatrix4(m);const v=f.getSize(this._tempBox2Size),_=Math.max(v.x,v.y);if(Math.max(d.x,d.y)!=0&&_!=0&&(d.z=v.z/Math.max(v.x,v.y)*Math.max(d.x,d.y)),t.lastScreenCoverage=Math.max(d.x,d.y,d.z),t.lastScreenspaceVolume.copy(d),t.lastScreenCoverage*=t.lastCentrality,re&&P.debugDrawLine){const u=this.tempMatrix.copy(this.projectionScreenMatrix);u.invert();const g=P.corner0,p=P.corner1,b=P.corner2,y=P.corner3;g.copy(this._box.min),p.copy(this._box.max),p.x=g.x,b.copy(this._box.max),b.y=g.y,y.copy(this._box.max);const E=(g.z+y.z)*.5;g.z=p.z=b.z=y.z=E,g.applyMatrix4(u),p.applyMatrix4(u),b.applyMatrix4(u),y.applyMatrix4(u),P.debugDrawLine(g,p,255),P.debugDrawLine(g,b,255),P.debugDrawLine(p,y,255),P.debugDrawLine(b,y,255)}let S=999;if(a&&t.lastScreenCoverage>0){for(let u=0;u<a.length;u++)if(a[u].density/t.lastScreenCoverage<o){S=u;break}}S<s&&(s=S)}}return s}};let C=P;c(C,"corner0",new j),c(C,"corner1",new j),c(C,"corner2",new j),c(C,"corner3",new j),c(C,"debugDrawLine");class we{constructor(){c(this,"lastLodLevel",0),c(this,"lastScreenCoverage",0),c(this,"lastScreenspaceVolume",new j),c(this,"lastCentrality",0)}}const te=Symbol("NEEDLE_mesh_lod"),U=Symbol("NEEDLE_texture_lod");function se(){const r=document.querySelector("model-viewer");if(!r)return;let e=null;for(let t=r;t!=null;t=Object.getPrototypeOf(t)){const o=Object.getOwnPropertySymbols(t).find(n=>n.toString()=="Symbol(renderer)");!e&&o!=null&&(e=r[o].threeRenderer)}if(e){console.log("Adding Needle LODs to modelviewer");const t=new C(e);t.plugins.push(new be(r)),t.enable()}}class be{constructor(e){c(this,"modelviewer"),this.modelviewer=e}onBeforeUpdateLOD(e,t,o,n){this.tryParseMeshLOD(t,n),this.tryParseTextureLOD(t,n)}getUrl(){return this.modelviewer.getAttribute("src")}tryGetCurrentGLTF(e){return e._currentGLTF}tryParseTextureLOD(e,t){if(t[U]==!0)return;t[U]=!0;const o=this.tryGetCurrentGLTF(e),n=this.getUrl();if(!n){console.error("No url found in modelviewer");return}if(o&&t.material){let s=function(a){var l,d,m;if(a[U]==!0)return;a[U]=!0,a.userData&&(a.userData.LOD=-1);const f=Object.keys(a);for(let v=0;v<f.length;v++){const _=f[v],S=a[_];if(S?.isTexture===!0){const u=(d=(l=S.userData)==null?void 0:l.associations)==null?void 0:d.textures,g=o.parser.json.textures[u];if((m=g.extensions)!=null&&m[A]){const p=g.extensions[A];p&&n&&w.registerTexture(n,S,p.lods.length,p)}}}};const i=t.material;if(Array.isArray(i))for(const a of i)s(a);else s(i)}}tryParseMeshLOD(e,t){var o,n;if(t[te]==!0)return;t[te]=!0;const s=this.getUrl();if(!s){console.error("No url found in modelviewer");return}const i=(n=(o=t.userData)==null?void 0:o.gltfExtensions)==null?void 0:n[A];if(i&&s){const a=t.uuid;w.registerMesh(s,a,t,0,i.lods.length,i)}}}document.addEventListener("DOMContentLoaded",()=>{Me()});function Me(){se()}export{A as EXTENSION_NAME,C as LODsManager,w as NEEDLE_progressive,se as patchModelViewer,De as registerPlugin};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<title>Minimal Example</title>
|
|
5
|
+
<script type="importmap">
|
|
6
|
+
{
|
|
7
|
+
"imports": {
|
|
8
|
+
"three": "https://unpkg.com/three/build/three.module.js",
|
|
9
|
+
"three/": "https://unpkg.com/three/"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
|
|
14
|
+
<script type="module" src="./@needle-tools-gltf-progressive.min.js"></script>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<model-viewer src="https://engine.needle.tools/demos/lods/assets/ganesha.glb" ar
|
|
18
|
+
shadow-intensity="1" camera-controls touch-action="pan-y"></model-viewer>
|
|
19
|
+
|
|
20
|
+
<style>
|
|
21
|
+
model-viewer {
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100vh;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
26
|
+
|
|
27
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@needle-tools/gltf-progressive",
|
|
3
|
+
"version": "1.0.0-alpha",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"homepage": "https://needle.tools",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Needle",
|
|
10
|
+
"email": "hi@needle.tools",
|
|
11
|
+
"url": "https://needle.tools/"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build:dist": "vite build"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"three": ">= 0.160.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/three": "0.162.0",
|
|
21
|
+
"three": ">= 0.160.0",
|
|
22
|
+
"vite": "<= 4.3.9"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"registry": "https://registry.npmjs.org"
|
|
27
|
+
}
|
|
28
|
+
}
|