@speridlabs/visus 2.3.0 → 2.4.1

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/dist/main.umd.js CHANGED
@@ -1,59 +1,51 @@
1
- (function(C,D){typeof exports=="object"&&typeof module<"u"?D(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],D):(C=typeof globalThis<"u"?globalThis:C||self,D(C.Visus={},C.THREE))})(this,function(C,D){"use strict";var zt=Object.defineProperty;var Tt=(C,D,rt)=>D in C?zt(C,D,{enumerable:!0,configurable:!0,writable:!0,value:rt}):C[D]=rt;var u=(C,D,rt)=>Tt(C,typeof D!="symbol"?D+"":D,rt);if(typeof THREE>"u")throw new Error(`Visus UMD build requires Three.js as a global "THREE".
2
- Please include <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"><\/script> before loading visus/main.umd.js`);function rt(k){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(k){for(const o in k)if(o!=="default"){const t=Object.getOwnPropertyDescriptor(k,o);Object.defineProperty(e,o,t.get?t:{enumerable:!0,get:()=>k[o]})}}return e.default=k,Object.freeze(e)}const a=rt(D);class at{constructor(){u(this,"min",new a.Vector3(1/0,1/0,1/0));u(this,"max",new a.Vector3(-1/0,-1/0,-1/0));u(this,"center",new a.Vector3);u(this,"halfExtents",new a.Vector3)}reset(){this.min.set(1/0,1/0,1/0),this.max.set(-1/0,-1/0,-1/0),this.center.set(0,0,0),this.halfExtents.set(0,0,0)}expandByPoint(e){this.min.x=Math.min(this.min.x,e.x),this.min.y=Math.min(this.min.y,e.y),this.min.z=Math.min(this.min.z,e.z),this.max.x=Math.max(this.max.x,e.x),this.max.y=Math.max(this.max.y,e.y),this.max.z=Math.max(this.max.z,e.z),this.updateDerived()}expandByBox(e){this.min.x=Math.min(this.min.x,e.min.x),this.min.y=Math.min(this.min.y,e.min.y),this.min.z=Math.min(this.min.z,e.min.z),this.max.x=Math.max(this.max.x,e.max.x),this.max.y=Math.max(this.max.y,e.max.y),this.max.z=Math.max(this.max.z,e.max.z),this.updateDerived()}updateDerived(){this.center.addVectors(this.min,this.max).multiplyScalar(.5),this.halfExtents.subVectors(this.max,this.min).multiplyScalar(.5)}containsPoint(e){return e.x>=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y&&e.z>=this.min.z&&e.z<=this.max.z}toBox3(){return new a.Box3().set(this.min,this.max)}clone(){const e=new at;return e.min.copy(this.min),e.max.copy(this.max),e.center.copy(this.center),e.halfExtents.copy(this.halfExtents),e}}class ft{constructor(e=0){u(this,"numSplats",0);u(this,"positions");u(this,"rotations");u(this,"scales");u(this,"colors");u(this,"opacities");u(this,"boundingBox",new at);this.numSplats=e,this.allocateBuffers(e)}allocateBuffers(e){this.positions=new Float32Array(e*3),this.rotations=new Float32Array(e*4),this.scales=new Float32Array(e*3),this.colors=new Float32Array(e*3),this.opacities=new Float32Array(e)}setSplat(e,o,t,r,s,n){if(e>=this.numSplats)throw new Error(`Splat index out of bounds: ${e} >= ${this.numSplats}`);const i=e*3,l=e*4,x=e*3,b=e*3;this.positions[i]=o.x,this.positions[i+1]=o.y,this.positions[i+2]=o.z,this.rotations[l]=t.x,this.rotations[l+1]=t.y,this.rotations[l+2]=t.z,this.rotations[l+3]=t.w,this.scales[x]=r.x,this.scales[x+1]=r.y,this.scales[x+2]=r.z,this.colors[b]=s.r,this.colors[b+1]=s.g,this.colors[b+2]=s.b,this.opacities[e]=n}getSplat(e){if(e>=this.numSplats)throw new Error(`Splat index out of bounds: ${e} >= ${this.numSplats}`);const o=e*3,t=e*4,r=e*3,s=e*3;return{position:new a.Vector3(this.positions[o],this.positions[o+1],this.positions[o+2]),rotation:new a.Quaternion(this.rotations[t],this.rotations[t+1],this.rotations[t+2],this.rotations[t+3]),scale:new a.Vector3(Math.exp(this.scales[r]),Math.exp(this.scales[r+1]),Math.exp(this.scales[r+2])),color:new a.Color(this.colors[s],this.colors[s+1],this.colors[s+2]),opacity:this.opacities[e]}}calculateBoundingBox(){this.boundingBox.reset();const e=new a.Vector3;for(let o=0;o<this.numSplats;o++){const t=o*3;e.set(this.positions[t],this.positions[t+1],this.positions[t+2]),this.boundingBox.expandByPoint(e)}return this.boundingBox}createDebugGeometry(){const e=new a.BufferGeometry;e.setAttribute("position",new a.BufferAttribute(this.positions,3));const o=new Float32Array(this.numSplats*3),t=new a.Quaternion,r=new a.Euler;for(let s=0;s<this.numSplats;s++){const n=s*4,i=s*3;t.set(this.rotations[n],this.rotations[n+1],this.rotations[n+2],this.rotations[n+3]),r.setFromQuaternion(t),o[i]=r.x,o[i+1]=r.y,o[i+2]=r.z}return e.setAttribute("rotation",new a.BufferAttribute(o,3)),e.setAttribute("scale",new a.BufferAttribute(this.scales,3)),e.setAttribute("color",new a.BufferAttribute(this.colors,3)),e.setAttribute("opacity",new a.BufferAttribute(this.opacities,1)),e}}class pt extends a.EventDispatcher{constructor(){super();u(this,"worker");u(this,"centers",null);u(this,"orderTexture",null);u(this,"chunks",null);u(this,"lastCameraPosition",new a.Vector3);u(this,"lastCameraDirection",new a.Vector3);const o=this.createWorkerCode(),t=new Blob([o],{type:"application/javascript"});this.worker=new Worker(URL.createObjectURL(t)),this.worker.onmessage=this.onWorkerMessage.bind(this)}onWorkerMessage(o){if(!this.orderTexture||!this.orderTexture.image)return console.error("SplatSorter: Order texture not initialized!");const{order:t,count:r}=o.data,s=this.orderTexture.image.data;if(!(s instanceof Uint32Array))return console.error("SplatSorter: Order texture data is not a Uint32Array!");s.set(new Uint32Array(t)),this.orderTexture.source.data.updateRanges||(this.orderTexture.source.data.updateRanges=[]),this.orderTexture.needsUpdate=!0;const n=s.buffer.slice(0),i={order:n};this.worker.postMessage(i,[n]),this.dispatchEvent({type:"updated",count:r})}init(o,t,r,s=!1){if(!o||!(o.image.data instanceof Uint32Array))throw new Error("SplatSorter: Invalid orderTexture provided. Must be DataTexture with Uint32Array data.");if(!t||t.length%3!==0)throw new Error("SplatSorter: Invalid centers array provided. Length must be multiple of 3.");if(o.image.data.length<t.length/3)throw new Error("SplatSorter: orderTexture data buffer is smaller than the number of splats.");if(t.buffer.byteLength===0)throw new Error("SplatSorter: positions buffer is detached (likely React StrictMode + cached asset). ");const n=t.length/3;this.orderTexture=o,s?this.centers=null:this.centers=t.slice();const i=this.orderTexture.image.data;for(let h=0;h<n;h++)i[h]=h;this.orderTexture.source.data.updateRanges||(this.orderTexture.source.data.updateRanges=[]),this.orderTexture.needsUpdate=!0;const l=i.buffer.slice(0),x=s?t.buffer:t.buffer.slice(0),b={order:l,centers:x},S=[l,x];if(r){this.chunks=r.slice();const h=this.chunks.buffer.slice(0);b.chunks=h,S.push(h)}this.worker.postMessage(b,S),queueMicrotask(()=>{this.dispatchEvent({type:"updated",count:n})})}setMapping(o){if(!this.centers)return console.warn("SplatSorter: Cannot set mapping before initialization.");let t;const r=[];if(!o){const l=this.centers.buffer.slice(0);return t={centers:l,mapping:null},r.push(l),this.worker.postMessage(t,r)}const s=new Float32Array(o.length*3);for(let l=0;l<o.length;l++){const x=o[l];if(x*3+2>=this.centers.length){console.warn(`SplatSorter: Mapping index ${x} out of bounds.`);continue}const b=x*3,S=l*3;s[S+0]=this.centers[b+0],s[S+1]=this.centers[b+1],s[S+2]=this.centers[b+2]}const n=s.buffer.slice(0),i=o.buffer.slice(0);t={centers:n,mapping:i},r.push(n,i),this.worker.postMessage(t,r)}setCamera(o,t){const r=this.lastCameraPosition.distanceToSquared(o)>1e-7,s=this.lastCameraDirection.dot(t)<.9999;if(!r&&!s)return;this.lastCameraPosition.copy(o),this.lastCameraDirection.copy(t);const n={cameraPosition:{x:o.x,y:o.y,z:o.z},cameraDirection:{x:t.x,y:t.y,z:t.z}};this.worker.postMessage(n)}dispose(){this.worker&&this.worker.terminate(),this.orderTexture=null,this.centers=null,this.chunks=null}createWorkerCode(){return`(${(function(){let t=null,r=null,s=null,n=null,i=null,l=null,x=!1;const b={x:0,y:0,z:0},S={x:0,y:0,z:0},h={x:0,y:0,z:0},y={x:0,y:0,z:0};let v=null,I=null;const A=32,W=new Array(A).fill(0),N=new Array(A).fill(0),j=new Array(A).fill(0),ct=(w,d,E)=>{for(;w<=d;){const M=d+w>>1,p=E(M);if(p>0)w=M+1;else if(p<0)d=M-1;else return M}return~w},H=()=>{if(!t||!r||!i||!l)return;if(r.length===0){const c={order:t.buffer,count:0};self.postMessage(c,[t.buffer]),t=null;return}const w=i.x,d=i.y,E=i.z,M=l.x,p=l.y,z=l.z,_=1e-4,X=Math.abs(w-b.x)>_||Math.abs(d-b.y)>_||Math.abs(E-b.z)>_,Q=Math.abs(M-S.x)>_||Math.abs(p-S.y)>_||Math.abs(z-S.z)>_;if(!x&&!X&&!Q)return;x=!1,b.x=w,b.y=d,b.z=E,S.x=M,S.y=p,S.z=z;let K=1/0,R=-1/0;for(let c=0;c<8;++c){const g=c&1?h.x:y.x,B=c&2?h.y:y.y,m=c&4?h.z:y.z,T=g*M+B*p+m*z;K=Math.min(K,T),R=Math.max(R,T)}const F=r.length/3,Z=R-K,U=(1<<Math.max(10,Math.min(20,Math.ceil(Math.log2(F/4)))))+1;if((!v||v.length!==F)&&(v=new Uint32Array(F)),!I||I.length!==U?I=new Uint32Array(U):I.fill(0),Z<1e-7){for(let c=0;c<F;++c)v[c]=0;I[0]=F}else if(s&&s.length>0){const c=s.length/6;W.fill(0);for(let m=0;m<c;++m){const T=m*6,V=s[T+0],q=s[T+1],L=s[T+2],P=s[T+3],O=V*M+q*p+L*z-K,G=O-P,$=O+P,tt=Math.max(0,Math.floor(G*A/Z)),et=Math.min(A,Math.ceil($*A/Z));for(let Y=tt;Y<et;++Y)W[Y]++}let g=0;for(let m=0;m<A;++m)g+=W[m];j[0]=0,N[0]=0;for(let m=1;m<A;++m)j[m-1]=W[m-1]/g*U>>>0,N[m]=N[m-1]+j[m-1];j[A-1]=W[A-1]/g*U>>>0;const B=Z/A;for(let m=0;m<F;++m){const T=m*3,V=r[T+0],q=r[T+1],L=r[T+2],P=V*M+q*p+L*z,G=(R-P)/B,$=Math.max(0,Math.min(A-1,Math.floor(G))),tt=G-$,et=N[$]+j[$]*tt>>>0,Y=Math.min(et,U-1);v[m]=Y,I[Y]++}}else{const c=(U-1)/Z;for(let g=0;g<F;++g){const B=g*3,m=r[B+0],T=r[B+1],V=r[B+2],q=m*M+T*p+V*z,P=(R-q)*c>>>0,O=Math.min(P,U-1);v[g]=O,I[O]++}}for(let c=1;c<U;c++)I[c]+=I[c-1];for(let c=F-1;c>=0;c--){const g=v[c],B=--I[g];t[B]=n?n[c]:c}const it=w*M+d*p+E*z,ot=c=>{if(!t)return-1/0;const g=t[c],B=g;if(!r||B*3+2>=r.length)return-1/0;const m=B*3;return r[m]*M+r[m+1]*p+r[m+2]*z-it};let nt=F;if(F>0&&ot(F-1)<0){const c=ct(0,F-1,ot);nt=c<0?~c:c}const f={order:t.buffer,count:nt};self.postMessage(f,[t.buffer]),t=null};self.onmessage=w=>{const d=w.data;d.order&&(t=new Uint32Array(d.order));let E=!1;if(d.centers&&(r=new Float32Array(d.centers),E=!0,x=!0),Object.prototype.hasOwnProperty.call(d,"mapping")&&(n=d.mapping?new Uint32Array(d.mapping):null,x=!0),d.chunks){if(s=new Float32Array(d.chunks),s.length>0&&s[3]>0)for(let M=0;M<s.length/6;++M){const p=M*6,z=s[p+0],_=s[p+1],X=s[p+2],Q=s[p+3],K=s[p+4],R=s[p+5];s[p+0]=(z+Q)*.5,s[p+1]=(_+K)*.5,s[p+2]=(X+R)*.5,s[p+3]=Math.sqrt((Q-z)**2+(K-_)**2+(R-X)**2)*.5}x=!0}if(E&&r&&r.length>0){h.x=y.x=r[0],h.y=y.y=r[1],h.z=y.z=r[2];for(let M=1;M<r.length/3;M++){const p=M*3;h.x=Math.min(h.x,r[p+0]),y.x=Math.max(y.x,r[p+0]),h.y=Math.min(h.y,r[p+1]),y.y=Math.max(y.y,r[p+1]),h.z=Math.min(h.z,r[p+2]),y.z=Math.max(y.z,r[p+2])}}else E&&r&&r.length===0&&(h.x=y.x=h.y=y.y=h.z=y.z=0);d.cameraPosition&&(i=d.cameraPosition),d.cameraDirection&&(l=d.cameraDirection),H()}}).toString()})();`}}const wt=(k,e)=>{const o=mt(k),t=mt(e);return o|t<<16};function mt(k){const e=new Float32Array([k]),t=new Int32Array(e.buffer)[0];let r=t>>16&32768,s=t>>12&2047;const n=t>>23&255;return n<103?r:n>142?(r|=31744,r|=(n===255?0:1)&&t&8388607,r):n<113?(s|=2048,r|=(s>>114-n)+(s>>113-n&1),r):(r|=n-112<<10|s>>1,r+=s&1,r)}const bt=new ArrayBuffer(4),xt=new DataView(bt),Mt=k=>(xt.setUint32(0,k,!0),xt.getFloat32(0,!0));class gt{constructor(e){u(this,"textureWidth");u(this,"textureHeight");u(this,"transformA");u(this,"transformB");u(this,"colorTexture");u(this,"orderTexture");const o=e.numSplats;this.textureWidth=Math.ceil(Math.sqrt(o)),this.textureHeight=Math.ceil(o/this.textureWidth),this.transformA=this.createTransformATexture(e),this.transformB=this.createTransformBTexture(e),this.colorTexture=this.createColorTexture(e),this.orderTexture=this.createOrderTexture(o)}createTransformATexture(e){const o=e.numSplats,t=new Float32Array(this.textureWidth*this.textureHeight*4);for(let s=0;s<o;s++){const n=s*4,i=s*3,l=s*4;t[n]=e.positions[i],t[n+1]=e.positions[i+1],t[n+2]=e.positions[i+2];const x=e.rotations[l+0],b=e.rotations[l+1],S=wt(x,b);t[n+3]=Mt(S)}const r=new a.DataTexture(t,this.textureWidth,this.textureHeight,a.RGBAFormat,a.FloatType);return r.magFilter=a.NearestFilter,r.minFilter=a.NearestFilter,r.source.data.updateRanges||(r.source.data.updateRanges=[]),r.needsUpdate=!0,r}createTransformBTexture(e){const o=e.numSplats,t=new Float32Array(this.textureWidth*this.textureHeight*4);for(let s=0;s<o;s++){const n=s*4,i=s*3,l=s*4;t[n]=e.scales[i],t[n+1]=e.scales[i+1],t[n+2]=e.scales[i+2],t[n+3]=e.rotations[l+2]}const r=new a.DataTexture(t,this.textureWidth,this.textureHeight,a.RGBAFormat,a.FloatType);return r.magFilter=a.NearestFilter,r.minFilter=a.NearestFilter,r.source.data.updateRanges||(r.source.data.updateRanges=[]),r.needsUpdate=!0,r}createColorTexture(e){const o=e.numSplats,t=new Float32Array(this.textureWidth*this.textureHeight*4);for(let s=0;s<o;s++){const n=s*4,i=s*3;t[n]=e.colors[i],t[n+1]=e.colors[i+1],t[n+2]=e.colors[i+2],t[n+3]=e.opacities[s]}const r=new a.DataTexture(t,this.textureWidth,this.textureHeight,a.RGBAFormat,a.FloatType);return r.source.data.updateRanges||(r.source.data.updateRanges=[]),r.needsUpdate=!0,r}createOrderTexture(e){const o=new Uint32Array(this.textureWidth*this.textureHeight);for(let r=0;r<e;r++)o[r]=r;const t=new a.DataTexture(o,this.textureWidth,this.textureHeight,a.RedIntegerFormat,a.UnsignedIntType);return t.source.data.updateRanges||(t.source.data.updateRanges=[]),t.needsUpdate=!0,t}dispose(){this.transformA.dispose(),this.transformB.dispose(),this.colorTexture.dispose(),this.orderTexture.dispose()}}const St=`
1
+ (function(G,te){typeof exports=="object"&&typeof module<"u"?te(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],te):(G=typeof globalThis<"u"?globalThis:G||self,te(G.Visus={},G.THREE))})(this,function(G,te){"use strict";var pt=Object.defineProperty;var xt=(G,te,fe)=>te in G?pt(G,te,{enumerable:!0,configurable:!0,writable:!0,value:fe}):G[te]=fe;var w=(G,te,fe)=>xt(G,typeof te!="symbol"?te+"":te,fe);if(typeof THREE>"u")throw new Error(`Visus UMD build requires Three.js as a global "THREE".
2
+ Please include <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"><\/script> before loading visus/main.umd.js`);function fe(a){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const t in a)if(t!=="default"){const s=Object.getOwnPropertyDescriptor(a,t);Object.defineProperty(e,t,s.get?s:{enumerable:!0,get:()=>a[t]})}}return e.default=a,Object.freeze(e)}const h=fe(te);class ge{constructor(){w(this,"min",new h.Vector3(1/0,1/0,1/0));w(this,"max",new h.Vector3(-1/0,-1/0,-1/0));w(this,"center",new h.Vector3);w(this,"halfExtents",new h.Vector3)}reset(){this.min.set(1/0,1/0,1/0),this.max.set(-1/0,-1/0,-1/0),this.center.set(0,0,0),this.halfExtents.set(0,0,0)}expandByPoint(e){this.min.x=Math.min(this.min.x,e.x),this.min.y=Math.min(this.min.y,e.y),this.min.z=Math.min(this.min.z,e.z),this.max.x=Math.max(this.max.x,e.x),this.max.y=Math.max(this.max.y,e.y),this.max.z=Math.max(this.max.z,e.z),this.updateDerived()}expandByBox(e){this.min.x=Math.min(this.min.x,e.min.x),this.min.y=Math.min(this.min.y,e.min.y),this.min.z=Math.min(this.min.z,e.min.z),this.max.x=Math.max(this.max.x,e.max.x),this.max.y=Math.max(this.max.y,e.max.y),this.max.z=Math.max(this.max.z,e.max.z),this.updateDerived()}updateDerived(){this.center.addVectors(this.min,this.max).multiplyScalar(.5),this.halfExtents.subVectors(this.max,this.min).multiplyScalar(.5)}containsPoint(e){return e.x>=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y&&e.z>=this.min.z&&e.z<=this.max.z}toBox3(){return new h.Box3().set(this.min,this.max)}clone(){const e=new ge;return e.min.copy(this.min),e.max.copy(this.max),e.center.copy(this.center),e.halfExtents.copy(this.halfExtents),e}}class je{constructor(e=0){w(this,"numSplats",0);w(this,"positions");w(this,"rotations");w(this,"scales");w(this,"colors");w(this,"opacities");w(this,"boundingBox",new ge);this.numSplats=e,this.allocateBuffers(e)}allocateBuffers(e){this.positions=new Float32Array(e*3),this.rotations=new Float32Array(e*4),this.scales=new Float32Array(e*3),this.colors=new Float32Array(e*3),this.opacities=new Float32Array(e)}setSplat(e,t,s,r,n,o){if(e>=this.numSplats)throw new Error(`Splat index out of bounds: ${e} >= ${this.numSplats}`);const i=e*3,u=e*4,m=e*3,x=e*3;this.positions[i]=t.x,this.positions[i+1]=t.y,this.positions[i+2]=t.z,this.rotations[u]=s.x,this.rotations[u+1]=s.y,this.rotations[u+2]=s.z,this.rotations[u+3]=s.w,this.scales[m]=r.x,this.scales[m+1]=r.y,this.scales[m+2]=r.z,this.colors[x]=n.r,this.colors[x+1]=n.g,this.colors[x+2]=n.b,this.opacities[e]=o}getSplat(e){if(e>=this.numSplats)throw new Error(`Splat index out of bounds: ${e} >= ${this.numSplats}`);const t=e*3,s=e*4,r=e*3,n=e*3;return{position:new h.Vector3(this.positions[t],this.positions[t+1],this.positions[t+2]),rotation:new h.Quaternion(this.rotations[s],this.rotations[s+1],this.rotations[s+2],this.rotations[s+3]),scale:new h.Vector3(Math.exp(this.scales[r]),Math.exp(this.scales[r+1]),Math.exp(this.scales[r+2])),color:new h.Color(this.colors[n],this.colors[n+1],this.colors[n+2]),opacity:this.opacities[e]}}calculateBoundingBox(){this.boundingBox.reset();const e=new h.Vector3;for(let t=0;t<this.numSplats;t++){const s=t*3;e.set(this.positions[s],this.positions[s+1],this.positions[s+2]),this.boundingBox.expandByPoint(e)}return this.boundingBox}createDebugGeometry(){const e=new h.BufferGeometry;e.setAttribute("position",new h.BufferAttribute(this.positions,3));const t=new Float32Array(this.numSplats*3),s=new h.Quaternion,r=new h.Euler;for(let n=0;n<this.numSplats;n++){const o=n*4,i=n*3;s.set(this.rotations[o],this.rotations[o+1],this.rotations[o+2],this.rotations[o+3]),r.setFromQuaternion(s),t[i]=r.x,t[i+1]=r.y,t[i+2]=r.z}return e.setAttribute("rotation",new h.BufferAttribute(t,3)),e.setAttribute("scale",new h.BufferAttribute(this.scales,3)),e.setAttribute("color",new h.BufferAttribute(this.colors,3)),e.setAttribute("opacity",new h.BufferAttribute(this.opacities,1)),e}dispose(){this.numSplats=0,this.scales=new Float32Array(0),this.colors=new Float32Array(0),this.positions=new Float32Array(0),this.rotations=new Float32Array(0),this.opacities=new Float32Array(0)}}class qe{constructor(e,t,s){w(this,"ranges");w(this,"numSplats",0);w(this,"boundingBox",new ge);w(this,"sh0");w(this,"quats");w(this,"scales");w(this,"means_l");w(this,"means_u");this.ranges=t,this.numSplats=e,this.sh0=s.sh0,this.quats=s.quats,this.scales=s.scales,this.means_l=s.means_l,this.means_u=s.means_u,this.boundingBox=this.calculateBoundingBox()}calculateBoundingBox(){this.boundingBox.reset();const{mins:e,maxs:t}=this.ranges.means,s=r=>Math.sign(r)*(Math.exp(Math.abs(r))-1);return this.boundingBox.center.set((s(e[0])+s(t[0]))*.5,(s(e[1])+s(t[1]))*.5,(s(e[2])+s(t[2]))*.5),this.boundingBox.halfExtents.set((s(t[0])-s(e[0]))*.5,(s(t[1])-s(e[1]))*.5,(s(t[2])-s(e[2]))*.5),this.boundingBox}dispose(){this.sh0.dispose(),this.quats.dispose(),this.scales.dispose(),this.means_l.dispose(),this.means_u.dispose()}}class Me{constructor(e,t,s,r,n,o,i,u){w(this,"numSplats");w(this,"textureWidth");w(this,"textureHeight");w(this,"ranges");w(this,"centers");w(this,"boundingBox");w(this,"packedColor");w(this,"packedGeometry");this.numSplats=e,this.textureWidth=t,this.textureHeight=s,this.boundingBox=u,this.ranges=i,this.centers=r,this.packedColor=o,this.packedGeometry=n}dispose(){this.centers=new Float32Array(0),this.packedColor=new Uint8Array(0),this.packedGeometry=new Uint32Array(0)}}class Fe extends h.EventDispatcher{constructor(){super();w(this,"worker");w(this,"centers",null);w(this,"orderTexture",null);w(this,"chunks",null);w(this,"lastCameraPosition",new h.Vector3);w(this,"lastCameraDirection",new h.Vector3);const t=this.createWorkerCode(),s=new Blob([t],{type:"application/javascript"});this.worker=new Worker(URL.createObjectURL(s)),this.worker.onmessage=this.onWorkerMessage.bind(this)}onWorkerMessage(t){if(!this.orderTexture||!this.orderTexture.image)return console.error("SplatSorter: Order texture not initialized!");const{order:s,count:r}=t.data,n=this.orderTexture.image.data;if(!(n instanceof Uint32Array))return console.error("SplatSorter: Order texture data is not a Uint32Array!");n.set(new Uint32Array(s)),this.orderTexture.source.data.updateRanges||(this.orderTexture.source.data.updateRanges=[]),this.orderTexture.needsUpdate=!0;const o=n.buffer.slice(0),i={order:o};this.worker.postMessage(i,[o]),this.dispatchEvent({type:"updated",count:r})}init(t,s,r,n=!1){if(!t||!(t.image.data instanceof Uint32Array))throw new Error("SplatSorter: Invalid orderTexture provided. Must be DataTexture with Uint32Array data.");if(!s||s.length%3!==0)throw new Error("SplatSorter: Invalid centers array provided. Length must be multiple of 3.");if(t.image.data.length<s.length/3)throw new Error("SplatSorter: orderTexture data buffer is smaller than the number of splats.");if(s.buffer.byteLength===0)throw new Error("SplatSorter: positions buffer is detached (likely React StrictMode + cached asset). ");const o=s.length/3;this.orderTexture=t,n?this.centers=null:this.centers=s.slice();const i=this.orderTexture.image.data;for(let l=0;l<o;l++)i[l]=l;this.orderTexture.source.data.updateRanges||(this.orderTexture.source.data.updateRanges=[]),this.orderTexture.needsUpdate=!0;const u=i.buffer.slice(0),m=n?s.buffer:s.buffer.slice(0),x={order:u,centers:m},c=[u,m];if(r){this.chunks=r.slice();const l=this.chunks.buffer.slice(0);x.chunks=l,c.push(l)}this.worker.postMessage(x,c),queueMicrotask(()=>{this.dispatchEvent({type:"updated",count:o})})}setMapping(t){if(!this.centers)return console.warn("SplatSorter: Cannot set mapping before initialization.");let s;const r=[];if(!t){const u=this.centers.buffer.slice(0);return s={centers:u,mapping:null},r.push(u),this.worker.postMessage(s,r)}const n=new Float32Array(t.length*3);for(let u=0;u<t.length;u++){const m=t[u];if(m*3+2>=this.centers.length){console.warn(`SplatSorter: Mapping index ${m} out of bounds.`);continue}const x=m*3,c=u*3;n[c+0]=this.centers[x+0],n[c+1]=this.centers[x+1],n[c+2]=this.centers[x+2]}const o=n.buffer.slice(0),i=t.buffer.slice(0);s={centers:o,mapping:i},r.push(o,i),this.worker.postMessage(s,r)}setCamera(t,s){const r=this.lastCameraPosition.distanceToSquared(t)>1e-7,n=this.lastCameraDirection.dot(s)<.9999;if(!r&&!n)return;this.lastCameraPosition.copy(t),this.lastCameraDirection.copy(s);const o={cameraPosition:{x:t.x,y:t.y,z:t.z},cameraDirection:{x:s.x,y:s.y,z:s.z}};this.worker.postMessage(o)}dispose(){this.worker&&this.worker.terminate(),this.orderTexture=null,this.centers=null,this.chunks=null}createWorkerCode(){return`(${(function(){let s=null,r=null,n=null,o=null,i=null,u=null,m=!1;const x={x:0,y:0,z:0},c={x:0,y:0,z:0},l={x:0,y:0,z:0},p={x:0,y:0,z:0};let y=null,M=null;const v=32,S=new Array(v).fill(0),k=new Array(v).fill(0),B=new Array(v).fill(0),H=(b,A,C)=>{for(;b<=A;){const g=A+b>>1,d=C(g);if(d>0)b=g+1;else if(d<0)A=g-1;else return g}return~b},R=()=>{if(!s||!r||!i||!u)return;if(r.length===0){const f={order:s.buffer,count:0};self.postMessage(f,[s.buffer]),s=null;return}const b=i.x,A=i.y,C=i.z,g=u.x,d=u.y,I=u.z,F=1e-4,T=Math.abs(b-x.x)>F||Math.abs(A-x.y)>F||Math.abs(C-x.z)>F,j=Math.abs(g-c.x)>F||Math.abs(d-c.y)>F||Math.abs(I-c.z)>F;if(!m&&!T&&!j)return;m=!1,x.x=b,x.y=A,x.z=C,c.x=g,c.y=d,c.z=I;let U=1/0,q=-1/0;for(let f=0;f<8;++f){const E=f&1?l.x:p.x,V=f&2?l.y:p.y,D=f&4?l.z:p.z,O=E*g+V*d+D*I;U=Math.min(U,O),q=Math.max(q,O)}const P=r.length/3,K=q-U,L=(1<<Math.max(10,Math.min(20,Math.ceil(Math.log2(P/4)))))+1;if((!y||y.length!==P)&&(y=new Uint32Array(P)),!M||M.length!==L?M=new Uint32Array(L):M.fill(0),K<1e-7){for(let f=0;f<P;++f)y[f]=0;M[0]=P}else if(n&&n.length>0){const f=n.length/6;S.fill(0);for(let D=0;D<f;++D){const O=D*6,$=n[O+0],X=n[O+1],J=n[O+2],N=n[O+3],ee=$*g+X*d+J*I-U,oe=ee-N,ie=ee+N,me=Math.max(0,Math.floor(oe*v/K)),de=Math.min(v,Math.ceil(ie*v/K));for(let le=me;le<de;++le)S[le]++}let E=0;for(let D=0;D<v;++D)E+=S[D];B[0]=0,k[0]=0;for(let D=1;D<v;++D)B[D-1]=S[D-1]/E*L>>>0,k[D]=k[D-1]+B[D-1];B[v-1]=S[v-1]/E*L>>>0;const V=K/v;for(let D=0;D<P;++D){const O=D*3,$=r[O+0],X=r[O+1],J=r[O+2],N=$*g+X*d+J*I,oe=(q-N)/V,ie=Math.max(0,Math.min(v-1,Math.floor(oe))),me=oe-ie,de=k[ie]+B[ie]*me>>>0,le=Math.min(de,L-1);y[D]=le,M[le]++}}else{const f=(L-1)/K;for(let E=0;E<P;++E){const V=E*3,D=r[V+0],O=r[V+1],$=r[V+2],X=D*g+O*d+$*I,N=(q-X)*f>>>0,ee=Math.min(N,L-1);y[E]=ee,M[ee]++}}for(let f=1;f<L;f++)M[f]+=M[f-1];for(let f=P-1;f>=0;f--){const E=y[f],V=--M[E];s[V]=o?o[f]:f}const Q=b*g+A*d+C*I,Z=f=>{if(!s)return-1/0;const E=s[f],V=E;if(!r||V*3+2>=r.length)return-1/0;const D=V*3;return r[D]*g+r[D+1]*d+r[D+2]*I-Q};let Y=P;if(P>0&&Z(P-1)<0){const f=H(0,P-1,Z);Y=f<0?~f:f}const z={order:s.buffer,count:Y};self.postMessage(z,[s.buffer]),s=null};self.onmessage=b=>{const A=b.data;A.order&&(s=new Uint32Array(A.order));let C=!1;if(A.centers&&(r=new Float32Array(A.centers),C=!0,m=!0),Object.prototype.hasOwnProperty.call(A,"mapping")&&(o=A.mapping?new Uint32Array(A.mapping):null,m=!0),A.chunks){if(n=new Float32Array(A.chunks),n.length>0&&n[3]>0)for(let g=0;g<n.length/6;++g){const d=g*6,I=n[d+0],F=n[d+1],T=n[d+2],j=n[d+3],U=n[d+4],q=n[d+5];n[d+0]=(I+j)*.5,n[d+1]=(F+U)*.5,n[d+2]=(T+q)*.5,n[d+3]=Math.sqrt((j-I)**2+(U-F)**2+(q-T)**2)*.5}m=!0}if(C&&r&&r.length>0){l.x=p.x=r[0],l.y=p.y=r[1],l.z=p.z=r[2];for(let g=1;g<r.length/3;g++){const d=g*3;l.x=Math.min(l.x,r[d+0]),p.x=Math.max(p.x,r[d+0]),l.y=Math.min(l.y,r[d+1]),p.y=Math.max(p.y,r[d+1]),l.z=Math.min(l.z,r[d+2]),p.z=Math.max(p.z,r[d+2])}}else C&&r&&r.length===0&&(l.x=p.x=l.y=p.y=l.z=p.z=0);A.cameraPosition&&(i=A.cameraPosition),A.cameraDirection&&(u=A.cameraDirection),R()}}).toString()})();`}}class Pe{constructor(e){w(this,"packedGeometry");w(this,"packedColor");w(this,"orderTexture");w(this,"width");w(this,"height");this.width=e.textureWidth,this.height=e.textureHeight,this.packedColor=this.createColorTexture(e),this.packedGeometry=this.createGeometryTexture(e),this.orderTexture=this.createOrderTexture(e.numSplats)}createGeometryTexture(e){const t=new h.DataTexture(e.packedGeometry,this.width,this.height,h.RGBAIntegerFormat,h.UnsignedIntType);return t.internalFormat="RGBA32UI",t.minFilter=h.NearestFilter,t.magFilter=h.NearestFilter,t.needsUpdate=!0,t}createColorTexture(e){const t=new h.DataTexture(e.packedColor,this.width,this.height,h.RGBAFormat,h.UnsignedByteType);return t.minFilter=h.NearestFilter,t.magFilter=h.NearestFilter,t.needsUpdate=!0,t}createOrderTexture(e){const t=new Uint32Array(this.width*this.height);for(let r=0;r<e;r++)t[r]=r;const s=new h.DataTexture(t,this.width,this.height,h.RedIntegerFormat,h.UnsignedIntType);return s.minFilter=h.NearestFilter,s.magFilter=h.NearestFilter,s.needsUpdate=!0,s}dispose(){this.packedGeometry.dispose(),this.packedColor.dispose(),this.orderTexture.dispose()}}const Ne=`
3
3
  precision highp float;
4
4
  precision highp int;
5
5
  precision highp usampler2D;
6
6
  precision highp sampler2D;
7
7
 
8
- // --- Uniforms (Provided by Three.js) ---
9
- // uniform mat4 modelViewMatrix; // Don't redeclare
10
- // uniform mat4 projectionMatrix; // Don't redeclare
11
- uniform vec2 viewport; // Viewport dimensions (width, height)
12
- uniform uint numSplats; // Total number of splats potentially renderable by sorter
8
+ uniform int texWidth;
9
+ uniform vec2 viewport;
10
+ uniform uint numSplats;
13
11
 
14
- // Textures
15
- uniform highp sampler2D transformA; // vec4: pos.xyz, packed_rot.yz
16
- uniform highp sampler2D transformB; // vec4: log_scale.xyz, rot.x
17
- uniform highp sampler2D splatColor; // vec4: color.rgb, opacity
18
- uniform highp usampler2D splatOrder; // uint: original splat index
12
+ // --- Unified Packed Textures ---
13
+ uniform usampler2D packedGeometry; // RGBA32UI: Geometry (Pos, Rot, Scale)
14
+ uniform sampler2D packedColor; // RGBA8: Color (RGB) + Opacity (A)
15
+ uniform usampler2D splatOrder; // R32UI: Sort Index
19
16
 
20
- // --- Attributes ---
21
- // Instancing attribute: base index for this block of splats
22
- attribute uint splatInstanceIndex;
17
+ // --- Decompression Ranges ---
18
+ uniform vec3 meansMin;
19
+ uniform vec3 meansMax;
20
+ uniform vec3 scalesMin;
21
+ uniform vec3 scalesMax;
22
+ uniform vec3 sh0Min;
23
+ uniform vec3 sh0Max;
23
24
 
24
- // Per-vertex attribute of the quad (Built-in, don't redeclare 'position')
25
- // attribute vec3 position; // Use the built-in 'position'
26
- // position.xy: corner coords (-1..1)
27
- // position.z: index within the instance block (0..INSTANCE_BUFFER_SIZE-1)
25
+ // --- Attributes ---
26
+ in uint splatInstanceIndex;
28
27
 
29
- // --- Varyings ---
30
- varying vec4 vColor;
31
- varying vec2 vUv; // For gaussian calculation in fragment shader
28
+ // --- Outputs ---
29
+ out vec4 vColor;
30
+ out vec2 vUv;
32
31
 
33
32
  // --- Constants ---
34
- #define INSTANCE_BUFFER_SIZE 128.0 // Must match the size in SplatMesh
33
+ const vec4 DISCARD_VERTEX = vec4(0.0, 0.0, 2.0, 1.0);
34
+ const float SH_C0 = 0.28209479177387814; // 1/(2*sqrt(pi))
35
+ const float QUAT_NORM = 1.41421356237; // sqrt(2)
35
36
 
36
37
  // --- Helper Functions ---
37
38
 
38
- // Use GLSL built-in unpackHalf2x16 for proper half-float unpacking
39
-
40
- // Reconstruct quaternion from packed components (xy_packed, z)
41
- // Returns (w,x,y,z) format for quatToMat3 compatibility
42
- vec4 unpackRotation(float xy_packed, float z) {
43
- vec2 xy = unpackHalf2x16(floatBitsToUint(xy_packed));
44
- float x = xy.x;
45
- float y = xy.y;
46
- float w_squared = 1.0 - x*x - y*y - z*z;
47
- float w = (w_squared < 0.0) ? 0.0 : sqrt(w_squared);
48
- return vec4(w, x, y, z); // Return (w,x,y,z)
39
+ // Extract raw bytes as integers (no /255 )
40
+ uvec4 unpackBytes(uint v) {
41
+ return (uvec4(v) >> uvec4(24u, 16u, 8u, 0u)) & 0xffu;
49
42
  }
50
43
 
51
- // Convert quaternion to 3x3 rotation matrix
52
44
  mat3 quatToMat3(vec4 R) {
53
45
  vec4 R2 = R + R;
54
46
  float X = R2.x * R.w;
55
- vec4 Y = R2.y * R;
56
- vec4 Z = R2.z * R;
47
+ vec4 Y = R2.y * R;
48
+ vec4 Z = R2.z * R;
57
49
  float W = R2.w * R.w;
58
50
 
59
51
  return mat3(
@@ -69,196 +61,173 @@ mat3 quatToMat3(vec4 R) {
69
61
  );
70
62
  }
71
63
 
72
- // Calculate model-space covariance components
73
64
  void getModelCovariance(vec3 scale, vec4 rotation, out vec3 covA, out vec3 covB) {
74
65
  mat3 rot = quatToMat3(rotation);
75
-
76
- // M = S * R (scale COLUMNS, then transpose)
77
66
  mat3 M = transpose(mat3(
78
- scale.x * rot[0], // scale.x * column 0
79
- scale.y * rot[1], // scale.y * column 1
80
- scale.z * rot[2] // scale.z * column 2
67
+ scale.x * rot[0],
68
+ scale.y * rot[1],
69
+ scale.z * rot[2]
81
70
  ));
82
71
 
83
72
  covA = vec3(dot(M[0], M[0]), dot(M[0], M[1]), dot(M[0], M[2]));
84
73
  covB = vec3(dot(M[1], M[1]), dot(M[1], M[2]), dot(M[2], M[2]));
85
74
  }
86
75
 
87
- // --- Main Function ---
88
- void main(void) {
89
- // Calculate the final splat index for this vertex
90
- uint instanceOffset = uint(position.z); // Read index within instance from Z
91
- uint orderedSplatIndex = splatInstanceIndex + instanceOffset;
76
+ // --- Main ---
77
+ void main() {
78
+ // Calculate Instance ID & Source Data UV
79
+ uint instanceOffset = uint(position.z);
80
+ uint orderIndex = splatInstanceIndex + instanceOffset;
92
81
 
93
- // Discard if this splat index is beyond the sorted count
94
- if (orderedSplatIndex >= numSplats) {
95
- gl_Position = vec4(2.0, 2.0, 2.0, 1.0); // Off-screen
82
+ if (orderIndex >= numSplats) {
83
+ gl_Position = DISCARD_VERTEX;
96
84
  return;
97
85
  }
98
86
 
99
- // --- Source Data Lookup ---
100
- uint texWidth = uint(textureSize(splatOrder, 0).x);
101
- ivec2 orderUV = ivec2(orderedSplatIndex % texWidth, orderedSplatIndex / texWidth);
102
- uint originalSplatIndex = texelFetch(splatOrder, orderUV, 0).r;
103
-
104
- ivec2 dataUV = ivec2(originalSplatIndex % texWidth, originalSplatIndex / texWidth);
105
-
106
- // Fetch data from textures
107
- vec4 texTransformA = texelFetch(transformA, dataUV, 0);
108
- vec4 texTransformB = texelFetch(transformB, dataUV, 0);
109
- vec4 texColor = texelFetch(splatColor, dataUV, 0);
110
-
111
- vec3 splatPosition = texTransformA.xyz;
112
- vec3 splatLogScale = texTransformB.xyz; // Assume stored as log scale
113
- vec4 splatRotation = unpackRotation(texTransformA.w, texTransformB.w); // Unpack rot xy, z
114
- vec3 splatColor = texColor.rgb;
115
- float splatOpacity = texColor.a;
116
-
117
- vec3 splatScale = exp(splatLogScale); // Convert log scale to actual scale
118
-
119
- // --- Center Projection ---
120
-
121
- // Compute normalized view-space center
122
- vec4 centerClipRaw = modelViewMatrix * vec4(splatPosition, 1.0);
123
- vec3 centerView = centerClipRaw.xyz / centerClipRaw.w;
124
-
125
- // Early out if splat is behind the camera
126
- if (centerView.z > -0.2) { // Use small epsilon
127
- gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
87
+ uint w = uint(texWidth);
88
+ ivec2 orderUV = ivec2(int(orderIndex % w), int(orderIndex / w));
89
+
90
+ // Fetch original splat ID from the order texture
91
+ uint splatID = texelFetch(splatOrder, orderUV, 0).r;
92
+ ivec2 dataUV = ivec2(int(splatID % w), int(splatID / w));
93
+
94
+ // Fetch Packed Data
95
+ // x: Means Low, y: Means High, z: Rotation, w: Scale
96
+ uvec4 geom = texelFetch(packedGeometry, dataUV, 0);
97
+ vec4 colorRaw = texelFetch(packedColor, dataUV, 0);
98
+
99
+ // Decompress Position (Means) (assume world-space)
100
+ // Reconstruct 16-bit values from two 8-bit channels
101
+ uvec3 lo = unpackBytes(geom.x).xyz;
102
+ uvec3 hi = unpackBytes(geom.y).xyz;
103
+
104
+ // (hi << 8) | lo => 0..65535 then normalize
105
+ vec3 meansNorm = vec3((hi << 8u) | lo) * (1.0 / 65535.0);
106
+ vec3 modelCenter = mix(meansMin, meansMax, meansNorm);
107
+
108
+ // Decompress Rotation (Quaternions)
109
+ uvec4 qB = unpackBytes(geom.z);
110
+ vec3 abc = (vec3(qB.xyz) * (1.0 / 255.0) - 0.5) * QUAT_NORM;
111
+ float d = sqrt(max(0.0, 1.0 - dot(abc, abc)));
112
+ uint mode = qB.w; // already 0..3
113
+
114
+
115
+ // Reconstruct Quaternion (x, y, z, w)
116
+ // quat in (w,x,y,z)
117
+ vec4 quat;
118
+ if (mode == 0u) quat = vec4(d, abc); // (w, x, y, z)
119
+ else if (mode == 1u) quat = vec4(abc.x, d, abc.yz); // stored (w,y,z)
120
+ else if (mode == 2u) quat = vec4(abc.xy, d, abc.z); // stored (w,x,z)
121
+ else quat = vec4(abc, d); // stored (w,x,y)
122
+
123
+ // Decompress Scale
124
+ uvec3 sB = unpackBytes(geom.w).xyz;
125
+ vec3 sNorm = vec3(sB) * (1.0 / 255.0);
126
+ vec3 sLerp = mix(scalesMin, scalesMax, sNorm);
127
+ vec3 scale = exp(sLerp); // log scale stored
128
+
129
+ // Decompress Color & Opacity
130
+ vec4 clr;
131
+ vec3 sh = mix(sh0Min, sh0Max, colorRaw.rgb);
132
+ clr.rgb = vec3(0.5) + sh * SH_C0;
133
+ clr.a = colorRaw.a; // Opacity passed directly (linear or sigmoid handled by loader)
134
+
135
+ // --- Standard Gaussian Splat Projection ---
136
+
137
+ // View Space Setup
138
+ mat4 modelView = modelViewMatrix;
139
+ vec4 centerView = modelView * vec4(modelCenter, 1.0);
140
+
141
+ // Cull behind camera
142
+ if (centerView.z > -0.2) {
143
+ gl_Position = DISCARD_VERTEX;
128
144
  return;
129
145
  }
130
146
 
131
- // Project to clip space
132
- vec4 centerClip = projectionMatrix * centerClipRaw;
133
-
134
- // --- Covariance Calculation & Projection ---
135
-
136
- // Calculating model space covariance (Vrk)
147
+ // Covariance in 3D
137
148
  vec3 covA, covB;
138
- getModelCovariance(splatScale, splatRotation, covA, covB);
139
- mat3 Vrk = mat3(
140
- covA.x, covA.y, covA.z,
141
- covA.y, covB.x, covB.y, // covB.x is Vrk[1][1]
142
- covA.z, covB.y, covB.z // covB.y is Vrk[1][2], covB.z is Vrk[2][2]
143
- );
149
+ getModelCovariance(scale, quat, covA, covB);
144
150
 
145
- // Calculate Jacobian J (screen space change wrt view space change)
146
- // Requres focal lenghts and view-space center depth (tz)
151
+ // Project to 2D
147
152
  float focalX = viewport.x * projectionMatrix[0][0];
148
153
  float focalY = viewport.y * projectionMatrix[1][1];
149
- float tz = centerView.z;
150
- float tz2 = tz * tz;
154
+ float J1 = focalX / centerView.z;
155
+ vec2 J2 = -J1 / centerView.z * centerView.xy;
151
156
 
152
- // Jacobian J = [fx/tz, 0, -fx*tx/tz^2]
153
- // [0, fy/tz, -fy*ty/tz^2]
154
157
  mat3 J = mat3(
155
- focalX / tz, 0.0, -focalX * centerView.x / tz2,
156
- 0.0, focalY / tz, -focalY * centerView.y / tz2,
157
- 0.0, 0.0, 0.0
158
+ J1, 0.0, J2.x,
159
+ 0.0, J1, J2.y,
160
+ 0.0, 0.0, 0.0
158
161
  );
159
162
 
160
- // Calculate W
161
- mat3 W = transpose(mat3(modelViewMatrix));
162
- // Calculate T = W * J
163
+ mat3 W = transpose(mat3(modelView));
163
164
  mat3 T = W * J;
164
- // Calculate Projected 2D Covariance: SigmaProj = transpose(T) * Vrk * T
165
- mat3 SigmaProj = transpose(T) * Vrk * T;
166
-
167
- // Extract 2D components and add bias
168
- float cov2D_11 = SigmaProj[0][0] + 0.3;
169
- float cov2D_12 = SigmaProj[0][1];
170
- float cov2D_22 = SigmaProj[1][1] + 0.3;
171
-
172
- // --- Calculate 2D Screen Space Rotation & Scale ---
173
-
174
- float det = cov2D_11 * cov2D_22 - cov2D_12 * cov2D_12;
175
- // Ensure determinant is non-negative before sqrt
176
- if (det <= 0.0) {
177
- gl_Position = vec4(2.0, 2.0, 2.0, 1.0); // Discard degenerated
178
- return;
165
+ mat3 Vrk = mat3(
166
+ covA.x, covA.y, covA.z,
167
+ covA.y, covB.x, covB.y,
168
+ covA.z, covB.y, covB.z
169
+ );
170
+ mat3 cov = transpose(T) * Vrk * T;
171
+
172
+ // Eigendecomposition for Quad dimensions
173
+ float diagonal1 = cov[0][0] + 0.3;
174
+ float offDiagonal = cov[0][1];
175
+ float diagonal2 = cov[1][1] + 0.3;
176
+
177
+ float mid = 0.5 * (diagonal1 + diagonal2);
178
+ float radius = length(vec2((diagonal1 - diagonal2) * 0.5, offDiagonal));
179
+ float lambda1 = mid + radius;
180
+ float lambda2 = max(mid - radius, 0.1);
181
+
182
+ // Screen space quad vectors
183
+ float vmin = min(1024.0, min(viewport.x, viewport.y)); // Safety clamp
184
+ float l1 = 2.0 * min(sqrt(2.0 * lambda1), vmin);
185
+ float l2 = 2.0 * min(sqrt(2.0 * lambda2), vmin);
186
+
187
+ // Discard tiny splats
188
+ if (l1 < 2.0 && l2 < 2.0) {
189
+ gl_Position = DISCARD_VERTEX;
190
+ return;
179
191
  }
180
192
 
181
- float trace = cov2D_11 + cov2D_22;
182
- float traceOver2 = 0.5 * trace;
183
- float discriminantSqrt = sqrt(max(traceOver2 * traceOver2 - det, 0.0)); // Avoid sqrt(negative)
184
-
185
- float lambda1 = traceOver2 + discriminantSqrt; // Larger eigenvalue
186
- float lambda2 = max(0.1, traceOver2 - discriminantSqrt); // Smaller eigenvalue, clamped
193
+ vec2 diagVec = normalize(vec2(offDiagonal, lambda1 - diagonal1));
194
+ vec2 v1 = l1 * diagVec;
195
+ vec2 v2 = l2 * vec2(diagVec.y, -diagVec.x);
187
196
 
188
- // Compute eigenvectors
189
-
190
- vec2 v1_eigen, v2_eigen;
197
+ // Vertex Position (Quad Expansion)
198
+ vec4 centerProj = projectionMatrix * centerView;
199
+ vec2 c = centerProj.ww / viewport; // Correction for clip space
191
200
 
192
- // Handle diagonal case
193
- if(abs(cov2D_12) < 1e-16) {
194
- v1_eigen = vec2(1.0, 0.0);
195
- } else {
196
- v1_eigen = normalize(vec2(cov2D_12, lambda1 - cov2D_11)); // diagonal choice
197
- }
198
-
199
- v2_eigen = vec2(-v1_eigen.y, v1_eigen.x); // Perpendicular eigenvector
200
-
201
- // Calculate SCALED vectors (l1, l2, incorparate factor)
202
- float scaleFactor = 2.0 * sqrt(2.0); // ~2.8284
203
- float l1 = sqrt(lambda1) * scaleFactor; // scaleX
204
- float l2 = sqrt(lambda2) * scaleFactor; // scaleY
205
-
206
- float vmin = min(512.0, min(viewport.x, viewport.y));
207
- l1 = min(l1, 2.0 * vmin);
208
- l2 = min(l2, 2.0 * vmin);
209
-
210
- // Early out tiny splats
211
- if (l1 < 2.0 && l2 < 2.0) { // Check if smaller than ~2 pixel
212
- gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
213
- return;
214
- }
215
-
216
- // Scaled eigenvectors for offset calculation
217
- vec2 v1_scaled = l1 * v1_eigen;
218
- vec2 v2_scaled = l2 * v2_eigen;
219
-
220
- // --- FRUSTUM CHECK (laxo, estilo PlayCanvas) ---
221
-
222
- vec2 c = centerClip.ww / viewport; // pixel to clip
223
- float r = max(l1, l2); // radius in pixels
224
-
225
- // Remove if the center - radius is already out of -w..w
226
- if (any(greaterThan(abs(centerClip.xy) - vec2(r) * c, centerClip.ww))) {
227
- gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
201
+ // Frustum cull
202
+ if (any(greaterThan(abs(centerProj.xy) - vec2(max(l1, l2)) * c, centerProj.ww))) {
203
+ gl_Position = DISCARD_VERTEX;
228
204
  return;
229
205
  }
230
206
 
231
- // --- END FRUSTUM CHECK ---
232
-
233
- // --- Final Vertex Position ---
234
-
235
- vec2 cornerOffset = position.xy; // (-1,-1) to (1,1)
236
-
237
- // Clip the quad so only high-alpha core is visible
238
- float alpha = max(splatOpacity, 1e-6); // avoid log(0)
239
- float clip = min(1.0, sqrt(-log(1.0 / 255.0 / alpha)) / 2.0);
240
-
241
- // Apply clip to the corner offset *before* calculating screen space offset
242
- vec2 clippedCornerOffset = cornerOffset * clip;
243
- vec2 screenOffsetPixels = clippedCornerOffset.x * v1_scaled + clippedCornerOffset.y * v2_scaled;
244
-
245
- // Convert pixel offset to clip space offset
246
- vec2 clipOffset = screenOffsetPixels * (centerClip.w / viewport);
247
-
248
-
249
- // Apply offset to center clip position
250
- gl_Position = centerClip + vec4(clipOffset, 0.0, 0.0);
251
-
252
- // --- Pass data to Fragment Shader ---
207
+ // Quad offset based on 'position' attribute (-1 to 1)
208
+ vec2 offset = (position.x * v1 + position.y * v2) * c;
209
+
210
+ // Corner Clipping (for gaussian shape)
211
+ // Assuming simple linear opacity here, otherwise inverse sigmoid logic required
212
+ float alpha = max(clr.a, 1.0/255.0);
213
+ float clip = min(1.0, sqrt(-log(1.0 / 255.0 / alpha)) / 2.0);
214
+
215
+ offset *= clip;
216
+ vec2 clippedUV = position.xy * clip;
253
217
 
254
- vColor = vec4(splatColor, splatOpacity);
255
- vUv = clippedCornerOffset;
218
+ gl_Position = centerProj + vec4(offset, 0.0, 0.0);
219
+
220
+ // Outputs
221
+ vUv = clippedUV;
222
+ vColor = vec4(max(clr.rgb, vec3(0.0)), clr.a);
256
223
  }
257
- `,Ct=`
224
+ `,He=`
258
225
  precision highp float;
259
226
 
260
- varying vec4 vColor; // Color and opacity passed from vertex shader
261
- varying vec2 vUv; // Quad UV coordinates (-1 to 1) passed from vertex shader
227
+ in vec4 vColor; // Color and opacity passed from vertex shader
228
+ in vec2 vUv; // Quad UV coordinates (-1 to 1) passed from vertex shader
229
+
230
+ out vec4 fragColor; // Output variable
262
231
 
263
232
  // Fast approximate e^x based on https://nic.schraudolph.org/pubs/Schraudolph99.pdf
264
233
  const float EXP_A = 12102203.0; // ≈ 2^23 / ln(2)
@@ -282,9 +251,9 @@ void main(void) {
282
251
  if (alpha < 1.0 / 255.0) discard; // Discard fragments with very low alpha
283
252
 
284
253
  // Premultiply color by alpha (required for correct blending)
285
- gl_FragColor = vec4(vColor.rgb * alpha, alpha);
254
+ fragColor = vec4(vColor.rgb * alpha, alpha);
286
255
  }
287
- `;class yt extends a.ShaderMaterial{constructor(e={}){const o={transformA:{value:null},transformB:{value:null},splatColor:{value:null},splatOrder:{value:null},viewport:{value:new a.Vector2(1,1)},numSplats:{value:0}};super({vertexShader:St,fragmentShader:Ct,uniforms:o,transparent:!0,blending:a.CustomBlending,blendSrc:a.OneFactor,blendDst:a.OneMinusSrcAlphaFactor,blendSrcAlpha:a.OneFactor,blendDstAlpha:a.OneMinusSrcAlphaFactor,blendEquation:a.AddEquation,blendEquationAlpha:a.AddEquation,depthTest:!0,depthWrite:!1,side:a.DoubleSide,alphaTest:e.alphaTest!==void 0?e.alphaTest:0,toneMapped:e.toneMapped!==void 0?e.toneMapped:!1}),e.alphaHash&&(this.alphaHash=!0,this.depthWrite=!0,this.blending=a.NoBlending)}updateViewport(e,o){this.uniforms.viewport.value.set(e,o)}setTransformA(e){this.uniforms.transformA.value=e}setTransformB(e){this.uniforms.transformB.value=e}setColorTexture(e){this.uniforms.splatColor.value=e}setOrderTexture(e){this.uniforms.splatOrder.value=e}setNumSplats(e){this.uniforms.numSplats.value=e}}const st=class st extends a.Mesh{constructor(o,t={}){const r=new yt(t),s=st.createInstancedGeometry(o.numSplats,st.INSTANCE_SIZE);super(s,r);u(this,"sorter");u(this,"splatData");u(this,"options");u(this,"textureManager");u(this,"material");u(this,"geometry");u(this,"lastCameraPositionLocal",new a.Vector3);u(this,"lastCameraDirectionLocal",new a.Vector3);u(this,"invModelMatrix",new a.Matrix4);u(this,"_vpW",-1);u(this,"_vpH",-1);u(this,"_size",new a.Vector2);u(this,"_camPosW",new a.Vector3);u(this,"_camDirW",new a.Vector3);u(this,"_camPosL",new a.Vector3);u(this,"_camDirL",new a.Vector3);u(this,"onSorterUpdated",o=>{const t=o.count;this.geometry.instanceCount=Math.ceil(t/st.INSTANCE_SIZE),this.material.setNumSplats(t)});this.geometry=s,this.material=r,this.splatData=o,this.frustumCulled=!1,this.options={autoSort:!0,...t},this.textureManager=new gt(o),this.sorter=new pt;let n=this.createChunks()||void 0;n===null&&console.warn("Visus: Could not create sorter chunks, bounding box might be invalid."),n=void 0,this.sorter.addEventListener("updated",this.onSorterUpdated),this.sorter.init(this.textureManager.orderTexture,this.splatData.positions,n??void 0,!this.options.keepSplatData),this.material.setTransformA(this.textureManager.transformA),this.material.setTransformB(this.textureManager.transformB),this.material.setColorTexture(this.textureManager.colorTexture),this.material.setOrderTexture(this.textureManager.orderTexture),this.material.setNumSplats(0),this.geometry.boundingBox=o.boundingBox.toBox3(),this.geometry.boundingSphere=new a.Sphere,this.geometry.boundingBox.getBoundingSphere(this.geometry.boundingSphere),this.options.keepSplatData||(this.splatData=null)}static createInstancedGeometry(o,t){const r=Math.ceil(o/t),s=new a.BufferGeometry,n=new Float32Array([-1,-1,0,1,-1,0,1,1,0,-1,1,0]),i=new Uint16Array([0,1,2,0,2,3]),l=new Float32Array(4*3*t);for(let h=0;h<t;h++){const y=h*4*3;for(let v=0;v<4;v++)l[y+v*3+0]=n[v*3+0],l[y+v*3+1]=n[v*3+1],l[y+v*3+2]=h}const x=new Uint32Array(6*t);for(let h=0;h<t;h++){const y=h*6,v=h*4;x[y+0]=i[0]+v,x[y+1]=i[1]+v,x[y+2]=i[2]+v,x[y+3]=i[3]+v,x[y+4]=i[4]+v,x[y+5]=i[5]+v}s.setAttribute("position",new a.BufferAttribute(l,3)),s.setIndex(new a.BufferAttribute(x,1));const b=new a.InstancedBufferGeometry;b.index=s.index,b.setAttribute("position",s.getAttribute("position"));const S=new Uint32Array(r);for(let h=0;h<r;h++)S[h]=h*t;return b.setAttribute("splatInstanceIndex",new a.InstancedBufferAttribute(S,1,!1)),b.instanceCount=0,b}createChunks(){if(!this.splatData)return null;const o=this.splatData.boundingBox;return o.min.x===1/0||o.max.x===-1/0?null:new Float32Array([o.min.x,o.min.y,o.min.z,o.max.x,o.max.y,o.max.z])}updateViewport(o,t){o===this._vpW&&t===this._vpH||(this._vpW=o,this._vpH=t,this.material.updateViewport(o,t))}sort(o){o.getWorldPosition(this._camPosW),o.getWorldDirection(this._camDirW),this.invModelMatrix.copy(this.matrixWorld).invert(),this._camPosL.copy(this._camPosW).applyMatrix4(this.invModelMatrix),this._camDirL.copy(this._camDirW).transformDirection(this.invModelMatrix);const t=this.lastCameraPositionLocal.distanceToSquared(this._camPosL)>1e-6,r=this.lastCameraDirectionLocal.dot(this._camDirL)<.999;this.options.autoSort&&(t||r)&&(this.lastCameraPositionLocal.copy(this._camPosL),this.lastCameraDirectionLocal.copy(this._camDirL),this.sorter.setCamera(this._camPosL,this._camDirL))}onBeforeRender(o,t,r){this.sort(r),o.getSize(this._size),this.updateViewport(this._size.x,this._size.y)}dispose(){this.sorter.removeEventListener("updated",this.onSorterUpdated),this.sorter.dispose(),this.geometry.dispose(),this.material.dispose(),this.textureManager.dispose()}};u(st,"INSTANCE_SIZE",128);let ut=st;class It extends a.Loader{constructor(o){super(o);u(this,"requestId",0);u(this,"worker");u(this,"pendingCallbacks",new Map);this.withCredentials=!0;const t=this.createWorkerCode(),r=new Blob([t],{type:"application/javascript"});this.worker=new Worker(URL.createObjectURL(r)),this.worker.onmessage=this.onWorkerMessage.bind(this)}onWorkerMessage(o){const{requestId:t,error:r,result:s}=o.data,n=this.pendingCallbacks.get(t);if(!n)return console.warn(`PlyLoader: Received response for unknown request ${t}`);if(this.pendingCallbacks.delete(t),r)return n.reject(new Error(r));if(!s)return n.reject(new Error("Worker returned no result"));try{const i=new ft(0);i.numSplats=s.numSplats,i.positions=new Float32Array(s.positions),i.rotations=new Float32Array(s.rotations),i.scales=new Float32Array(s.scales),i.colors=new Float32Array(s.colors),i.opacities=new Float32Array(s.opacities),i.boundingBox.min.set(s.boundingBox.minX,s.boundingBox.minY,s.boundingBox.minZ),i.boundingBox.max.set(s.boundingBox.maxX,s.boundingBox.maxY,s.boundingBox.maxZ),this.worker.terminate(),n.resolve(i)}catch(i){n.reject(i)}}load(o,t,r,s){const n=new a.FileLoader(this.manager);n.setResponseType("arraybuffer"),n.setRequestHeader(this.requestHeader),n.setPath(this.path),n.setWithCredentials(this.withCredentials),n.load(o,i=>{this.parseAsync(i).then(l=>{t&&t(l)}).catch(l=>{s?s(l):console.error(l),this.manager.itemError(o)})},r,s)}loadAsync(o,t){return new Promise((r,s)=>{const n=new a.FileLoader(this.manager);n.setResponseType("arraybuffer"),n.setRequestHeader(this.requestHeader),n.setPath(this.path),n.setWithCredentials(this.withCredentials),n.load(o,i=>{this.parseAsync(i).then(r).catch(l=>{s(l),this.manager.itemError(o)})},t,i=>{s(i),this.manager.itemError(o)})})}parseAsync(o){return new Promise((t,r)=>{const s=this.requestId++;this.pendingCallbacks.set(s,{resolve:t,reject:r});const n={requestId:s,buffer:o};this.worker.postMessage(n,[o])})}dispose(){this.worker&&this.worker.terminate(),this.pendingCallbacks.clear()}createWorkerCode(){return`(${(function(){self.onmessage=r=>{const{requestId:s,buffer:n}=r.data;try{const i=t(n),l={requestId:s,result:i};self.postMessage(l,[i.positions,i.rotations,i.scales,i.colors,i.opacities])}catch(i){const l={requestId:s,error:i instanceof Error?i.message:String(i)};self.postMessage(l)}};function t(r){const s=new TextDecoder,n=new Uint8Array(r),i=[112,108,121,10],l=`
256
+ `;class Re extends h.ShaderMaterial{constructor(e={}){const t={packedGeometry:{value:null},packedColor:{value:null},splatOrder:{value:null},meansMin:{value:new h.Vector3},meansMax:{value:new h.Vector3},scalesMin:{value:new h.Vector3},scalesMax:{value:new h.Vector3},sh0Min:{value:new h.Vector3},sh0Max:{value:new h.Vector3},texWidth:{value:0},viewport:{value:new h.Vector2(1,1)},numSplats:{value:0}};super({uniforms:t,vertexShader:Ne,fragmentShader:He,transparent:!0,glslVersion:h.GLSL3,blendSrc:h.OneFactor,blendSrcAlpha:h.OneFactor,blending:h.CustomBlending,blendEquation:h.AddEquation,blendEquationAlpha:h.AddEquation,blendDst:h.OneMinusSrcAlphaFactor,blendDstAlpha:h.OneMinusSrcAlphaFactor,depthTest:!0,depthWrite:!1,side:h.DoubleSide,alphaTest:e.alphaTest??0,toneMapped:e.toneMapped??!1}),e.alphaHash&&(this.alphaHash=!0,this.depthWrite=!0,this.blending=h.NoBlending)}setTexWidth(e){this.uniforms.texWidth.value=e|0}updateViewport(e,t){this.uniforms.viewport.value.set(e,t)}setPackedGeometry(e){this.uniforms.packedGeometry.value=e}setPackedColor(e){this.uniforms.packedColor.value=e}setOrderTexture(e){this.uniforms.splatOrder.value=e}setRanges(e){this.uniforms.meansMin.value.copy(e.means.min),this.uniforms.meansMax.value.copy(e.means.max),this.uniforms.scalesMin.value.copy(e.scales.min),this.uniforms.scalesMax.value.copy(e.scales.max),this.uniforms.sh0Min.value.copy(e.sh0.min),this.uniforms.sh0Max.value.copy(e.sh0.max)}setNumSplats(e){this.uniforms.numSplats.value=e}}const ye=class ye extends h.Mesh{constructor(t,s={}){const r=new Re(s),n=ye.createInstancedGeometry(t.numSplats,ye.INSTANCE_SIZE);super(n,r);w(this,"sorter");w(this,"options");w(this,"splatData");w(this,"textureManager");w(this,"material");w(this,"geometry");w(this,"lastCameraPositionLocal",new h.Vector3);w(this,"lastCameraDirectionLocal",new h.Vector3);w(this,"invModelMatrix",new h.Matrix4);w(this,"_vpW",-1);w(this,"_vpH",-1);w(this,"_size",new h.Vector2);w(this,"_camPosW",new h.Vector3);w(this,"_camDirW",new h.Vector3);w(this,"_camPosL",new h.Vector3);w(this,"_camDirL",new h.Vector3);w(this,"onSorterUpdated",t=>{const s=t.count;this.geometry.instanceCount=Math.ceil(s/ye.INSTANCE_SIZE),this.material.setNumSplats(s)});this.geometry=n,this.material=r,this.options={autoSort:!0,...s},this.splatData=t,this.frustumCulled=!1,this.sorter=new Fe,this.textureManager=new Pe(t);let o=this.createChunks()||void 0;o===null&&console.warn("Visus: Could not create sorter chunks, bounding box might be invalid."),o=void 0,this.sorter.addEventListener("updated",this.onSorterUpdated),this.sorter.init(this.textureManager.orderTexture,this.splatData.centers,o??void 0,!this.options.keepSplatData),this.material.setRanges(t.ranges),this.material.setTexWidth(t.textureWidth),this.material.setPackedColor(this.textureManager.packedColor),this.material.setOrderTexture(this.textureManager.orderTexture),this.material.setPackedGeometry(this.textureManager.packedGeometry),this.material.setNumSplats(0),this.geometry.boundingBox=t.boundingBox.toBox3(),this.geometry.boundingSphere=new h.Sphere,this.geometry.boundingBox.getBoundingSphere(this.geometry.boundingSphere),this.options.keepSplatData||(this.splatData=null)}static createInstancedGeometry(t,s){const r=Math.ceil(t/s),n=new h.BufferGeometry,o=new Float32Array([-1,-1,0,1,-1,0,1,1,0,-1,1,0]),i=new Uint16Array([0,1,2,0,2,3]),u=new Float32Array(4*3*s);for(let l=0;l<s;l++){const p=l*4*3;for(let y=0;y<4;y++)u[p+y*3+0]=o[y*3+0],u[p+y*3+1]=o[y*3+1],u[p+y*3+2]=l}const m=new Uint32Array(6*s);for(let l=0;l<s;l++){const p=l*6,y=l*4;m[p+0]=i[0]+y,m[p+1]=i[1]+y,m[p+2]=i[2]+y,m[p+3]=i[3]+y,m[p+4]=i[4]+y,m[p+5]=i[5]+y}n.setAttribute("position",new h.BufferAttribute(u,3)),n.setIndex(new h.BufferAttribute(m,1));const x=new h.InstancedBufferGeometry;x.index=n.index,x.setAttribute("position",n.getAttribute("position"));const c=new Uint32Array(r);for(let l=0;l<r;l++)c[l]=l*s;return x.setAttribute("splatInstanceIndex",new h.InstancedBufferAttribute(c,1,!1)),x.instanceCount=0,x}createChunks(){if(!this.splatData)return null;const t=this.splatData.boundingBox;return t.min.x===1/0||t.max.x===-1/0?null:new Float32Array([t.min.x,t.min.y,t.min.z,t.max.x,t.max.y,t.max.z])}updateViewport(t,s){t===this._vpW&&s===this._vpH||(this._vpW=t,this._vpH=s,this.material.updateViewport(t,s))}sort(t){t.getWorldPosition(this._camPosW),t.getWorldDirection(this._camDirW),this.invModelMatrix.copy(this.matrixWorld).invert(),this._camPosL.copy(this._camPosW).applyMatrix4(this.invModelMatrix),this._camDirL.copy(this._camDirW).transformDirection(this.invModelMatrix);const s=this.lastCameraPositionLocal.distanceToSquared(this._camPosL)>1e-6,r=this.lastCameraDirectionLocal.dot(this._camDirL)<.999;this.options.autoSort&&(s||r)&&(this.lastCameraPositionLocal.copy(this._camPosL),this.lastCameraDirectionLocal.copy(this._camDirL),this.sorter.setCamera(this._camPosL,this._camDirL))}onBeforeRender(t,s,r){this.sort(r),t.getSize(this._size),this.updateViewport(this._size.x,this._size.y)}dispose(){this.sorter.removeEventListener("updated",this.onSorterUpdated),this.sorter.dispose(),this.geometry.dispose(),this.material.dispose(),this.textureManager.dispose()}};w(ye,"INSTANCE_SIZE",128);let be=ye;const pe=.28209479177387814;class Se{static packPly(e){const t=e.numSplats,s=Math.ceil(Math.sqrt(t)),r=Math.ceil(t/s),n=s*r,{ranges:o,colorScale:i}=this.calculatePlyRanges(e),u=new Uint32Array(n*4),m=new Uint8Array(n*4),x=new h.Quaternion;for(let c=0;c<t;c++){const l=c*4,p=c*3,y=c*4,M=e.positions[p+0],v=e.positions[p+1],S=e.positions[p+2],k=this.clamp01(this.norm(M,o.means.min.x,o.means.max.x)),B=this.clamp01(this.norm(v,o.means.min.y,o.means.max.y)),H=this.clamp01(this.norm(S,o.means.min.z,o.means.max.z)),R=k*65535|0,b=B*65535|0,A=H*65535|0;u[l+0]=this.pack4Bytes(R&255,b&255,A&255,0),u[l+1]=this.pack4Bytes(R>>8,b>>8,A>>8,0),x.set(e.rotations[y],e.rotations[y+1],e.rotations[y+2],e.rotations[y+3]).normalize();let C=x.w,g=x.x,d=x.y,I=x.z;const F=Math.abs(C),T=Math.abs(g),j=Math.abs(d),U=Math.abs(I);let q=0,P=F;T>P&&(P=T,q=1),j>P&&(P=j,q=2),U>P&&(P=U,q=3),(q===0?C:q===1?g:q===2?d:I)<0&&(C=-C,g=-g,d=-d,I=-I);let W=0,L=0,Q=0;q===0&&(W=g,L=d,Q=I),q===1&&(W=C,L=d,Q=I),q===2&&(W=C,L=g,Q=I),q===3&&(W=C,L=g,Q=d);const Z=ie=>Math.min(255,Math.max(0,Math.round((.5+ie/Math.SQRT2)*255)));u[l+2]=this.pack4Bytes(Z(W),Z(L),Z(Q),q);const Y=this.norm(e.scales[p+0],o.scales.min.x,o.scales.max.x),z=this.norm(e.scales[p+1],o.scales.min.y,o.scales.max.y),f=this.norm(e.scales[p+2],o.scales.min.z,o.scales.max.z);u[l+3]=this.pack4Bytes(Math.floor(Y*255),Math.floor(z*255),Math.floor(f*255),0);const E=this.clamp01(e.colors[p+0]*i),V=this.clamp01(e.colors[p+1]*i),D=this.clamp01(e.colors[p+2]*i),O=(E-.5)/pe,$=(V-.5)/pe,X=(D-.5)/pe,J=this.clamp01(this.norm(O,o.sh0.min.x,o.sh0.max.x)),N=this.clamp01(this.norm($,o.sh0.min.y,o.sh0.max.y)),ee=this.clamp01(this.norm(X,o.sh0.min.z,o.sh0.max.z)),oe=this.clamp01(e.opacities[c]);m[l+0]=Math.round(J*255),m[l+1]=Math.round(N*255),m[l+2]=Math.round(ee*255),m[l+3]=Math.round(oe*255)}return new Me(t,s,r,e.positions,u,m,o,e.boundingBox)}static packSogs(e){const t=e.numSplats,s=Math.ceil(Math.sqrt(t)),r=Math.ceil(t/s),n=s*r,o=this.convertSogsRanges(e.ranges),i=e.ranges.means.mins[0],u=e.ranges.means.mins[1],m=e.ranges.means.mins[2],x=e.ranges.means.maxs[0],c=e.ranges.means.maxs[1],l=e.ranges.means.maxs[2],p=g=>Math.sign(g)*(Math.exp(Math.abs(g))-1),y=new h.Vector3(p(i),p(u),p(m)),M=new h.Vector3(p(x),p(c),p(l));o.means.min.copy(y),o.means.max.copy(M);const v=e,S=this.getPixels(v.means_l),k=this.getPixels(v.means_u),B=this.getPixels(v.quats),H=this.getPixels(v.scales),R=this.getPixels(v.sh0),b=new Uint32Array(n*4),A=new Uint8Array(n*4),C=new Float32Array(t*3);for(let g=0;g<t;g++){const d=g*4,I=k[d+0],F=S[d+0],T=k[d+1],j=S[d+1],U=k[d+2],q=S[d+2],P=(I<<8|F)/65535,K=(T<<8|j)/65535,W=(U<<8|q)/65535,L=this.sogsDecode(P,i,x),Q=this.sogsDecode(K,u,c),Z=this.sogsDecode(W,m,l);C[g*3+0]=L,C[g*3+1]=Q,C[g*3+2]=Z;const Y=this.clamp01(this.norm(L,y.x,M.x))*65535|0,z=this.clamp01(this.norm(Q,y.y,M.y))*65535|0,f=this.clamp01(this.norm(Z,y.z,M.z))*65535|0;b[d+0]=this.pack4Bytes(Y&255,z&255,f&255,0),b[d+1]=this.pack4Bytes(Y>>8,z>>8,f>>8,0),b[d+3]=this.pack4Bytes(H[d],H[d+1],H[d+2],0);const V=B[d+3]-252;b[d+2]=this.pack4Bytes(B[d+0],B[d+1],B[d+2],V),A[d+0]=R[d],A[d+1]=R[d+1],A[d+2]=R[d+2];const D=R[d+3]/255,O=e.ranges.sh0.mins[3]+(e.ranges.sh0.maxs[3]-e.ranges.sh0.mins[3])*D,$=1/(1+Math.exp(-O));A[d+3]=Math.round(Math.max(0,Math.min(1,$))*255)}return new Me(t,s,r,C,b,A,o,e.boundingBox)}static pack4Bytes(e,t,s,r){return(e&255)<<24|(t&255)<<16|(s&255)<<8|r&255}static getPixels(e){const t=e.image,s=t.width,r=t.height;if(t.data&&(t.data instanceof Uint8Array||t.data instanceof Uint8ClampedArray))return new Uint8Array(t.data);const n=document.createElement("canvas");n.width=s,n.height=r;const o=n.getContext("2d",{willReadFrequently:!0});if(!o)throw new Error("Canvas init failed");return o.drawImage(t,0,0),new Uint8Array(o.getImageData(0,0,s,r).data)}static convertSogsRanges(e){const t=s=>new h.Vector3(s[0],s[1],s[2]);return{sh0:{min:t(e.sh0.mins),max:t(e.sh0.maxs)},means:{min:t(e.means.mins),max:t(e.means.maxs)},scales:{min:t(e.scales.mins),max:t(e.scales.maxs)}}}static calculatePlyRanges(e){const t=new h.Vector3(1/0,1/0,1/0),s=new h.Vector3(-1/0,-1/0,-1/0),r=new h.Vector3(1/0,1/0,1/0),n=new h.Vector3(-1/0,-1/0,-1/0),o=new h.Vector3(1/0,1/0,1/0),i=new h.Vector3(-1/0,-1/0,-1/0);let u=0;for(let x=0;x<e.numSplats;x++){const c=x*3,l=e.positions[c+0],p=e.positions[c+1],y=e.positions[c+2];t.x=Math.min(t.x,l),s.x=Math.max(s.x,l),t.y=Math.min(t.y,p),s.y=Math.max(s.y,p),t.z=Math.min(t.z,y),s.z=Math.max(s.z,y),r.x=Math.min(r.x,e.scales[c]),n.x=Math.max(n.x,e.scales[c]),r.y=Math.min(r.y,e.scales[c+1]),n.y=Math.max(n.y,e.scales[c+1]),r.z=Math.min(r.z,e.scales[c+2]),n.z=Math.max(n.z,e.scales[c+2]),u=Math.max(u,e.colors[c],e.colors[c+1],e.colors[c+2])}const m=u>1.5?1/255:1;for(let x=0;x<e.numSplats;x++){const c=x*3,l=this.clamp01(e.colors[c+0]*m),p=this.clamp01(e.colors[c+1]*m),y=this.clamp01(e.colors[c+2]*m),M=(l-.5)/pe,v=(p-.5)/pe,S=(y-.5)/pe;o.x=Math.min(o.x,M),i.x=Math.max(i.x,M),o.y=Math.min(o.y,v),i.y=Math.max(i.y,v),o.z=Math.min(o.z,S),i.z=Math.max(i.z,S)}return{colorScale:m,ranges:{sh0:{min:o,max:i},means:{min:t,max:s},scales:{min:r,max:n}}}}static clamp01(e){return Math.max(0,Math.min(1,e))}static norm(e,t,s){return(e-t)/(s-t||1)}static sogsDecode(e,t,s){const r=t+(s-t)*e;return Math.sign(r)*(Math.exp(Math.abs(r))-1)}}class Ye extends h.Loader{constructor(t){super(t);w(this,"requestId",0);w(this,"worker");w(this,"pendingCallbacks",new Map);this.withCredentials=!0;const s=this.createWorkerCode(),r=new Blob([s],{type:"application/javascript"});this.worker=new Worker(URL.createObjectURL(r)),this.worker.onmessage=this.onWorkerMessage.bind(this)}onWorkerMessage(t){const{requestId:s,error:r,result:n}=t.data,o=this.pendingCallbacks.get(s);if(!o)return console.warn(`PlyLoader: Received response for unknown request ${s}`);if(this.pendingCallbacks.delete(s),r)return o.reject(new Error(r));if(!n)return o.reject(new Error("Worker returned no result"));try{const i=new je(0);i.numSplats=n.numSplats,i.positions=new Float32Array(n.positions),i.rotations=new Float32Array(n.rotations),i.scales=new Float32Array(n.scales),i.colors=new Float32Array(n.colors),i.opacities=new Float32Array(n.opacities),i.boundingBox.min.set(n.boundingBox.minX,n.boundingBox.minY,n.boundingBox.minZ),i.boundingBox.max.set(n.boundingBox.maxX,n.boundingBox.maxY,n.boundingBox.maxZ),this.worker.terminate();const u=Se.packPly(i);i.dispose(),o.resolve(u)}catch(i){o.reject(i)}}load(t,s,r,n){const o=new h.FileLoader(this.manager);o.setResponseType("arraybuffer"),o.setRequestHeader(this.requestHeader),o.setPath(this.path),o.setWithCredentials(this.withCredentials),o.load(t,i=>{this.parseAsync(i).then(u=>{s&&s(u)}).catch(u=>{n?n(u):console.error(u),this.manager.itemError(t)})},r,n)}loadAsync(t,s){return new Promise((r,n)=>{const o=new h.FileLoader(this.manager);o.setResponseType("arraybuffer"),o.setRequestHeader(this.requestHeader),o.setPath(this.path),o.setWithCredentials(this.withCredentials),o.load(t,i=>{this.parseAsync(i).then(r).catch(u=>{n(u),this.manager.itemError(t)})},s,i=>{n(i),this.manager.itemError(t)})})}parseAsync(t){return new Promise((s,r)=>{const n=this.requestId++;this.pendingCallbacks.set(n,{resolve:s,reject:r});const o={requestId:n,buffer:t};this.worker.postMessage(o,[t])})}dispose(){this.worker&&this.worker.terminate(),this.pendingCallbacks.clear()}createWorkerCode(){return`(${(function(){self.onmessage=r=>{const{requestId:n,buffer:o}=r.data;try{const i=s(o),u={requestId:n,result:i};self.postMessage(u,[i.positions,i.rotations,i.scales,i.colors,i.opacities])}catch(i){const u={requestId:n,error:i instanceof Error?i.message:String(i)};self.postMessage(u)}};function s(r){const n=new TextDecoder,o=new Uint8Array(r),i=[112,108,121,10],u=`
288
257
  end_header
289
- `;for(let f=0;f<i.length;f++)if(n[f]!==i[f])throw new Error("Invalid PLY file: Missing magic bytes");let x=0;for(let f=0;f<n.length-l.length;f++){let c=!0;for(let g=0;g<l.length;g++)if(n[f+g]!==l.charCodeAt(g)){c=!1;break}if(c){x=f+l.length;break}}if(x===0)throw new Error("Invalid PLY file: Could not find end of header");const S=s.decode(n.subarray(0,x)).split(`
290
- `),h=[];let y=null;for(let f=1;f<S.length;f++){const c=S[f].trim();if(c===""||c==="end_header")continue;const g=c.split(" ");switch(g[0]){case"format":y=g[1];break;case"element":h.push({name:g[1],count:parseInt(g[2],10),properties:[]});break;case"property":if(h.length===0)throw new Error("Invalid PLY file: Property without element");h[h.length-1].properties.push({type:g[1],name:g[2]});break}}if(y!=="binary_little_endian")throw new Error(`Unsupported PLY format: ${y}`);const v=h.find(f=>f.name==="vertex");if(!v)throw new Error("Invalid PLY file: No vertex element found");const I=v.count,A=new Float32Array(I*3),W=new Float32Array(I*4),N=new Float32Array(I*3),j=new Float32Array(I*3),ct=new Float32Array(I),H=new DataView(r);let w=x;const d=f=>v.properties.findIndex(c=>c.name===f),E=d("x"),M=d("y"),p=d("z"),z=[d("rot_0"),d("rot_1"),d("rot_2"),d("rot_3")],_=[d("scale_0"),d("scale_1"),d("scale_2")],X=[d("f_dc_0"),d("f_dc_1"),d("f_dc_2")],Q=d("opacity");if([E,M,p,...z,..._,...X,Q].some(f=>f===-1))throw new Error("Invalid PLY file: Missing required properties");const R=.28209479177387814,F=f=>{if(f>0)return 1/(1+Math.exp(-f));const c=Math.exp(f);return c/(1+c)};let Z=1/0,lt=1/0,U=1/0,it=-1/0,ot=-1/0,nt=-1/0;for(let f=0;f<I;f++){const c=[];for(let dt=0;dt<v.properties.length;dt++){const vt=v.properties[dt].type;let J;switch(vt){case"char":J=H.getInt8(w),w+=1;break;case"uchar":J=H.getUint8(w),w+=1;break;case"short":J=H.getInt16(w,!0),w+=2;break;case"ushort":J=H.getUint16(w,!0),w+=2;break;case"int":J=H.getInt32(w,!0),w+=4;break;case"uint":J=H.getUint32(w,!0),w+=4;break;case"float":J=H.getFloat32(w,!0),w+=4;break;case"double":J=H.getFloat64(w,!0),w+=8;break;default:throw new Error(`Unsupported property type: ${vt}`)}c.push(J)}const g=c[E],B=c[M],m=c[p],T=f*3;A[T]=g,A[T+1]=B,A[T+2]=m,Z=Math.min(Z,g),lt=Math.min(lt,B),U=Math.min(U,m),it=Math.max(it,g),ot=Math.max(ot,B),nt=Math.max(nt,m);let V=c[z[1]],q=c[z[2]],L=c[z[3]],P=c[z[0]];const O=Math.sqrt(V*V+q*q+L*L+P*P);O>0&&(V/=O,q/=O,L/=O,P/=O),P<0&&(V=-V,q=-q,L=-L,P=-P);const G=f*4;W[G]=V,W[G+1]=q,W[G+2]=L,W[G+3]=P;const $=f*3;N[$]=c[_[0]],N[$+1]=c[_[1]],N[$+2]=c[_[2]];let tt=.5+c[X[0]]*R,et=.5+c[X[1]]*R,Y=.5+c[X[2]]*R;tt=Math.max(0,Math.min(1,tt)),et=Math.max(0,Math.min(1,et)),Y=Math.max(0,Math.min(1,Y));const ht=f*3;j[ht]=tt,j[ht+1]=et,j[ht+2]=Y,ct[f]=F(c[Q])}return{numSplats:I,positions:A.buffer,rotations:W.buffer,scales:N.buffer,colors:j.buffer,opacities:ct.buffer,boundingBox:{minX:Z,minY:lt,minZ:U,maxX:it,maxY:ot,maxZ:nt}}}}).toString()})();`}}const At="0.3.0";C.BoundingBox=at,C.PlyLoader=It,C.SplatData=ft,C.SplatMaterial=yt,C.SplatMesh=ut,C.SplatSorter=pt,C.TextureManager=gt,C.VERSION=At,Object.defineProperty(C,Symbol.toStringTag,{value:"Module"})});
258
+ `;for(let z=0;z<i.length;z++)if(o[z]!==i[z])throw new Error("Invalid PLY file: Missing magic bytes");let m=0;for(let z=0;z<o.length-u.length;z++){let f=!0;for(let E=0;E<u.length;E++)if(o[z+E]!==u.charCodeAt(E)){f=!1;break}if(f){m=z+u.length;break}}if(m===0)throw new Error("Invalid PLY file: Could not find end of header");const c=n.decode(o.subarray(0,m)).split(`
259
+ `),l=[];let p=null;for(let z=1;z<c.length;z++){const f=c[z].trim();if(f===""||f==="end_header")continue;const E=f.split(" ");switch(E[0]){case"format":p=E[1];break;case"element":l.push({name:E[1],count:parseInt(E[2],10),properties:[]});break;case"property":if(l.length===0)throw new Error("Invalid PLY file: Property without element");l[l.length-1].properties.push({type:E[1],name:E[2]});break}}if(p!=="binary_little_endian")throw new Error(`Unsupported PLY format: ${p}`);const y=l.find(z=>z.name==="vertex");if(!y)throw new Error("Invalid PLY file: No vertex element found");const M=y.count,v=new Float32Array(M*3),S=new Float32Array(M*4),k=new Float32Array(M*3),B=new Float32Array(M*3),H=new Float32Array(M),R=new DataView(r);let b=m;const A=z=>y.properties.findIndex(f=>f.name===z),C=A("x"),g=A("y"),d=A("z"),I=[A("rot_0"),A("rot_1"),A("rot_2"),A("rot_3")],F=[A("scale_0"),A("scale_1"),A("scale_2")],T=[A("f_dc_0"),A("f_dc_1"),A("f_dc_2")],j=A("opacity");if([C,g,d,...I,...F,...T,j].some(z=>z===-1))throw new Error("Invalid PLY file: Missing required properties");const q=.28209479177387814,P=z=>{if(z>0)return 1/(1+Math.exp(-z));const f=Math.exp(z);return f/(1+f)};let K=1/0,W=1/0,L=1/0,Q=-1/0,Z=-1/0,Y=-1/0;for(let z=0;z<M;z++){const f=[];for(let Ee=0;Ee<y.properties.length;Ee++){const We=y.properties[Ee].type;let he;switch(We){case"char":he=R.getInt8(b),b+=1;break;case"uchar":he=R.getUint8(b),b+=1;break;case"short":he=R.getInt16(b,!0),b+=2;break;case"ushort":he=R.getUint16(b,!0),b+=2;break;case"int":he=R.getInt32(b,!0),b+=4;break;case"uint":he=R.getUint32(b,!0),b+=4;break;case"float":he=R.getFloat32(b,!0),b+=4;break;case"double":he=R.getFloat64(b,!0),b+=8;break;default:throw new Error(`Unsupported property type: ${We}`)}f.push(he)}const E=f[C],V=f[g],D=f[d],O=z*3;v[O]=E,v[O+1]=V,v[O+2]=D,K=Math.min(K,E),W=Math.min(W,V),L=Math.min(L,D),Q=Math.max(Q,E),Z=Math.max(Z,V),Y=Math.max(Y,D);let $=f[I[1]],X=f[I[2]],J=f[I[3]],N=f[I[0]];const ee=Math.sqrt($*$+X*X+J*J+N*N);ee>0&&($/=ee,X/=ee,J/=ee,N/=ee),N<0&&($=-$,X=-X,J=-J,N=-N);const oe=z*4;S[oe]=$,S[oe+1]=X,S[oe+2]=J,S[oe+3]=N;const ie=z*3;k[ie]=f[F[0]],k[ie+1]=f[F[1]],k[ie+2]=f[F[2]];let me=.5+f[T[0]]*q,de=.5+f[T[1]]*q,le=.5+f[T[2]]*q;me=Math.max(0,Math.min(1,me)),de=Math.max(0,Math.min(1,de)),le=Math.max(0,Math.min(1,le));const De=z*3;B[De]=me,B[De+1]=de,B[De+2]=le,H[z]=P(f[j])}return{numSplats:M,positions:v.buffer,rotations:S.buffer,scales:k.buffer,colors:B.buffer,opacities:H.buffer,boundingBox:{minX:K,minY:W,minZ:L,maxX:Q,maxY:Z,maxZ:Y}}}}).toString()})();`}}var se=Uint8Array,xe=Uint16Array,$e=Int32Array,Te=new se([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Ue=new se([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Ze=new se([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),_e=function(a,e){for(var t=new xe(31),s=0;s<31;++s)t[s]=e+=1<<a[s-1];for(var r=new $e(t[30]),s=1;s<30;++s)for(var n=t[s];n<t[s+1];++n)r[n]=n-t[s]<<5|s;return{b:t,r}},Ve=_e(Te,2),Oe=Ve.b,Xe=Ve.r;Oe[28]=258,Xe[258]=28;for(var Qe=_e(Ue,0),Je=Qe.b,Ae=new xe(32768),_=0;_<32768;++_){var ue=(_&43690)>>1|(_&21845)<<1;ue=(ue&52428)>>2|(ue&13107)<<2,ue=(ue&61680)>>4|(ue&3855)<<4,Ae[_]=((ue&65280)>>8|(ue&255)<<8)>>1}for(var we=function(a,e,t){for(var s=a.length,r=0,n=new xe(e);r<s;++r)a[r]&&++n[a[r]-1];var o=new xe(e);for(r=1;r<e;++r)o[r]=o[r-1]+n[r-1]<<1;var i;if(t){i=new xe(1<<e);var u=15-e;for(r=0;r<s;++r)if(a[r])for(var m=r<<4|a[r],x=e-a[r],c=o[a[r]-1]++<<x,l=c|(1<<x)-1;c<=l;++c)i[Ae[c]>>u]=m}else for(i=new xe(s),r=0;r<s;++r)a[r]&&(i[r]=Ae[o[a[r]-1]++]>>15-a[r]);return i},ve=new se(288),_=0;_<144;++_)ve[_]=8;for(var _=144;_<256;++_)ve[_]=9;for(var _=256;_<280;++_)ve[_]=7;for(var _=280;_<288;++_)ve[_]=8;for(var Le=new se(32),_=0;_<32;++_)Le[_]=5;var Ke=we(ve,9,1),et=we(Le,5,1),Ie=function(a){for(var e=a[0],t=1;t<a.length;++t)a[t]>e&&(e=a[t]);return e},ne=function(a,e,t){var s=e/8|0;return(a[s]|a[s+1]<<8)>>(e&7)&t},ke=function(a,e){var t=e/8|0;return(a[t]|a[t+1]<<8|a[t+2]<<16)>>(e&7)},tt=function(a){return(a+7)/8|0},Ce=function(a,e,t){return(e==null||e<0)&&(e=0),(t==null||t>a.length)&&(t=a.length),new se(a.subarray(e,t))},st=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],re=function(a,e,t){var s=new Error(e||st[a]);if(s.code=a,Error.captureStackTrace&&Error.captureStackTrace(s,re),!t)throw s;return s},rt=function(a,e,t,s){var r=a.length,n=s?s.length:0;if(!r||e.f&&!e.l)return t||new se(0);var o=!t,i=o||e.i!=2,u=e.i;o&&(t=new se(r*3));var m=function(X){var J=t.length;if(X>J){var N=new se(Math.max(J*2,X));N.set(t),t=N}},x=e.f||0,c=e.p||0,l=e.b||0,p=e.l,y=e.d,M=e.m,v=e.n,S=r*8;do{if(!p){x=ne(a,c,1);var k=ne(a,c+1,3);if(c+=3,k)if(k==1)p=Ke,y=et,M=9,v=5;else if(k==2){var b=ne(a,c,31)+257,A=ne(a,c+10,15)+4,C=b+ne(a,c+5,31)+1;c+=14;for(var g=new se(C),d=new se(19),I=0;I<A;++I)d[Ze[I]]=ne(a,c+I*3,7);c+=A*3;for(var F=Ie(d),T=(1<<F)-1,j=we(d,F,1),I=0;I<C;){var U=j[ne(a,c,T)];c+=U&15;var B=U>>4;if(B<16)g[I++]=B;else{var q=0,P=0;for(B==16?(P=3+ne(a,c,3),c+=2,q=g[I-1]):B==17?(P=3+ne(a,c,7),c+=3):B==18&&(P=11+ne(a,c,127),c+=7);P--;)g[I++]=q}}var K=g.subarray(0,b),W=g.subarray(b);M=Ie(K),v=Ie(W),p=we(K,M,1),y=we(W,v,1)}else re(1);else{var B=tt(c)+4,H=a[B-4]|a[B-3]<<8,R=B+H;if(R>r){u&&re(0);break}i&&m(l+H),t.set(a.subarray(B,R),l),e.b=l+=H,e.p=c=R*8,e.f=x;continue}if(c>S){u&&re(0);break}}i&&m(l+131072);for(var L=(1<<M)-1,Q=(1<<v)-1,Z=c;;Z=c){var q=p[ke(a,c)&L],Y=q>>4;if(c+=q&15,c>S){u&&re(0);break}if(q||re(2),Y<256)t[l++]=Y;else if(Y==256){Z=c,p=null;break}else{var z=Y-254;if(Y>264){var I=Y-257,f=Te[I];z=ne(a,c,(1<<f)-1)+Oe[I],c+=f}var E=y[ke(a,c)&Q],V=E>>4;E||re(3),c+=E&15;var W=Je[V];if(V>3){var f=Ue[V];W+=ke(a,c)&(1<<f)-1,c+=f}if(c>S){u&&re(0);break}i&&m(l+131072);var D=l+z;if(l<W){var O=n-W,$=Math.min(W,D);for(O+l<0&&re(3);l<$;++l)t[l]=s[O+l]}for(;l<D;++l)t[l]=t[l-W]}}e.l=p,e.p=Z,e.b=l,e.f=x,p&&(x=1,e.m=M,e.d=y,e.n=v)}while(!x);return l!=t.length&&o?Ce(t,0,l):t.subarray(0,l)},nt=new se(0),ce=function(a,e){return a[e]|a[e+1]<<8},ae=function(a,e){return(a[e]|a[e+1]<<8|a[e+2]<<16|a[e+3]<<24)>>>0},ze=function(a,e){return ae(a,e)+ae(a,e+4)*4294967296};function at(a,e){return rt(a,{i:2},e&&e.out,e&&e.dictionary)}var Be=typeof TextDecoder<"u"&&new TextDecoder,ot=0;try{Be.decode(nt,{stream:!0}),ot=1}catch{}var it=function(a){for(var e="",t=0;;){var s=a[t++],r=(s>127)+(s>223)+(s>239);if(t+r>a.length)return{s:e,r:Ce(a,t-1)};r?r==3?(s=((s&15)<<18|(a[t++]&63)<<12|(a[t++]&63)<<6|a[t++]&63)-65536,e+=String.fromCharCode(55296|s>>10,56320|s&1023)):r&1?e+=String.fromCharCode((s&31)<<6|a[t++]&63):e+=String.fromCharCode((s&15)<<12|(a[t++]&63)<<6|a[t++]&63):e+=String.fromCharCode(s)}};function ct(a,e){if(e){for(var t="",s=0;s<a.length;s+=16384)t+=String.fromCharCode.apply(null,a.subarray(s,s+16384));return t}else{if(Be)return Be.decode(a);var r=it(a),n=r.s,t=r.r;return t.length&&re(8),n}}var lt=function(a,e){return e+30+ce(a,e+26)+ce(a,e+28)},ht=function(a,e,t){var s=ce(a,e+28),r=ct(a.subarray(e+46,e+46+s),!(ce(a,e+8)&2048)),n=e+46+s,o=ae(a,e+20),i=t&&o==4294967295?ut(a,n):[o,ae(a,e+24),ae(a,e+42)],u=i[0],m=i[1],x=i[2];return[ce(a,e+10),u,m,r,n+ce(a,e+30)+ce(a,e+32),x]},ut=function(a,e){for(;ce(a,e)!=1;e+=4+ce(a,e+2));return[ze(a,e+12),ze(a,e+4),ze(a,e+20)]};function mt(a,e){for(var t={},s=a.length-22;ae(a,s)!=101010256;--s)(!s||a.length-s>65558)&&re(13);var r=ce(a,s+8);if(!r)return{};var n=ae(a,s+16),o=n==4294967295||r==65535;if(o){var i=ae(a,s-12);o=ae(a,i)==101075792,o&&(r=ae(a,i+32),n=ae(a,i+48))}for(var u=0;u<r;++u){var m=ht(a,n,o),x=m[0],c=m[1],l=m[2],p=m[3],y=m[4],M=m[5],v=lt(a,M);n=y,x?x==8?t[p]=at(a.subarray(v,v+c),{out:new se(l)}):re(14,"unknown compression type "+x):t[p]=Ce(a,v,v+c)}return t}const Ge=a=>{if(typeof a!="object"||a===null)throw new Error("Invalid SOGS metadata: not an object");const e=a,t=e.version??1;if(t!==1)throw new Error(`Unsupported SOGS version: ${String(t)}`);const s=M=>{const v=e[M];if(typeof v!="object"||v===null)throw new Error(`Invalid SOGS metadata section: ${M}`);return v},r=s("means"),n=s("sh0"),o=s("quats"),i=s("scales"),u=typeof e.count=="number"&&Number.isFinite(e.count)?e.count:void 0,m=Array.isArray(r.shape)?r.shape:void 0,x=m&&typeof m[0]=="number"?m[0]:void 0;if(u===void 0&&x===void 0)throw new Error("Invalid SOGS metadata: unable to determine splat count");if(u!==void 0&&x!==void 0&&u!==x)throw new Error("Inconsistent SOGS metadata: count does not match means.shape[0]");const c=u??x,l=(M,v)=>{const S=M.mins,k=M.maxs;if(!Array.isArray(S)||S.length!==3)throw new Error(`${v}.mins must be length-3`);if(!Array.isArray(k)||k.length!==3)throw new Error(`${v}.maxs must be length-3`);if(![...S,...k].every(B=>typeof B=="number"&&Number.isFinite(B)))throw new Error(`${v}.mins/maxs must be finite numbers`);return{mins:[S[0],S[1],S[2]],maxs:[k[0],k[1],k[2]]}},p=(M,v)=>{const S=M.mins,k=M.maxs;if(!Array.isArray(S)||S.length!==4)throw new Error(`${v}.mins must be length-4`);if(!Array.isArray(k)||k.length!==4)throw new Error(`${v}.maxs must be length-4`);if(![...S,...k].every(B=>typeof B=="number"&&Number.isFinite(B)))throw new Error(`${v}.mins/maxs must be finite numbers`);return{mins:[S[0],S[1],S[2],S[3]],maxs:[k[0],k[1],k[2],k[3]]}},y=(M,v)=>{const S=M.files;if(!Array.isArray(S))throw new Error(`${v}.files is not an array`);if(!S.every(k=>typeof k=="string"))throw new Error(`${v}.files contains non-strings`);return S};return{numSplats:c,files:{sh0:y(n,"sh0"),means:y(r,"means"),quats:y(o,"quats"),scales:y(i,"scales")},ranges:{sh0:p(n,"sh0"),means:l(r,"means"),scales:l(i,"scales")}}};class dt extends h.Loader{constructor(e){super(e),this.withCredentials=!0}isZipBuffer(e){if(e.byteLength<4)return!1;const t=new Uint8Array(e,0,4);return t[0]===80&&t[1]===75&&t[2]===3&&t[3]===4}load(e,t,s,r){const n=new h.FileLoader(this.manager);n.setResponseType("arraybuffer"),n.setRequestHeader(this.requestHeader),n.setPath(this.path),n.setWithCredentials(this.withCredentials),n.load(e,o=>{this.parseAsync(e,o).then(i=>{t&&t(i)}).catch(i=>{this.manager.itemError(e),r&&r(i),console.error("Error loading SOGS meta:",i)})},s,r)}loadAsync(e,t){return new Promise((s,r)=>{const n=new h.FileLoader(this.manager);n.setResponseType("arraybuffer"),n.setRequestHeader(this.requestHeader),n.setPath(this.path),n.setWithCredentials(this.withCredentials),n.load(e,o=>{this.parseAsync(e,o).then(s).catch(i=>{r(i),this.manager.itemError(e)})},t,o=>{r(o),this.manager.itemError(e)})})}async parseAsync(e,t){if(!(t instanceof ArrayBuffer))throw new Error("SOGS loader: expected ArrayBuffer payload");if(this.isZipBuffer(t))return this.parseZipAsync(t);const r=new TextDecoder("utf-8").decode(t),n=JSON.parse(r);return this.parseMetaAsync(e,n)}async parseMetaAsync(e,t){const{numSplats:s,files:r,ranges:n}=Ge(t),o=new h.TextureLoader(this.manager);o.setPath(this.path),o.setRequestHeader(this.requestHeader),o.setWithCredentials(this.withCredentials);const i=(S,k)=>{try{return new URL(k,S).toString()}catch{return S.substring(0,S.lastIndexOf("/")+1)+k}},u=(S,k)=>{const B=i(e,S);return new Promise((H,R)=>{o.load(B,b=>{b.generateMipmaps=!1,b.magFilter=h.NearestFilter,b.minFilter=h.NearestFilter,b.flipY=!1,H(b)},void 0,b=>{const A=b instanceof Error?b.message:String(b);R(new Error(`SOGS loader: failed to load ${k} (${S}): ${A}`))})})};if(!Array.isArray(r.sh0)||r.sh0.length<1)throw new Error("SOGS loader: files.sh0 must have at least 1 entry (sh0)");if(!Array.isArray(r.means)||r.means.length<2)throw new Error("SOGS loader: files.means must have at least 2 entries (means_l, means_u)");if(!Array.isArray(r.quats)||r.quats.length<1)throw new Error("SOGS loader: files.quats must have at least 1 entry");if(!Array.isArray(r.scales)||r.scales.length<1)throw new Error("SOGS loader: files.scales must have at least 1 entry");const[m,x,c,l,p]=await Promise.all([u(r.means[0],"means_l"),u(r.means[1],"means_u"),u(r.quats[0],"quats"),u(r.scales[0],"scales"),u(r.sh0[0],"sh0")]),y={means_l:m,means_u:x,quats:c,scales:l,sh0:p},M=new qe(s,n,y),v=Se.packSogs(M);return M.dispose(),v}async parseZipAsync(e){const t=mt(new Uint8Array(e)),s=Object.keys(t),r=s.find(C=>C.toLowerCase().endsWith("meta.json"))??null;if(!r)throw new Error("SOGS loader: zip missing meta.json");const n=r.includes("/")?r.slice(0,r.lastIndexOf("/")+1):"",o=new TextDecoder,i=JSON.parse(o.decode(t[r])),{numSplats:u,files:m,ranges:x}=Ge(i),c=C=>C.replace(/\\/g,"/").replace(/^\.?\//,""),l=C=>{const g=c(C),d=[c(n+g),g];for(const T of d){const j=t[T];if(j)return j}const I=g.split("/").pop(),F=s.find(T=>c(T).endsWith("/"+g))??s.find(T=>c(T).endsWith("/"+I))??s.find(T=>c(T)===I)??null;if(F)return t[F];throw new Error(`SOGS loader: zip missing file "${C}"`)},p=C=>{const g=C.toLowerCase();return g.endsWith(".png")?"image/png":g.endsWith(".jpg")||g.endsWith(".jpeg")?"image/jpeg":g.endsWith(".webp")?"image/webp":"application/octet-stream"},y=new h.TextureLoader(this.manager);y.setRequestHeader(this.requestHeader),y.setWithCredentials(this.withCredentials);const M=(C,g)=>{const d=l(C),I=new Blob([d],{type:p(C)}),F=URL.createObjectURL(I);return new Promise((T,j)=>{y.load(F,U=>{URL.revokeObjectURL(F),U.generateMipmaps=!1,U.magFilter=h.NearestFilter,U.minFilter=h.NearestFilter,U.flipY=!1,T(U)},void 0,U=>{URL.revokeObjectURL(F);const q=U instanceof Error?U.message:String(U);j(new Error(`SOGS loader: failed to load ${g} from zip (${C}): ${q}`))})})};if(!Array.isArray(m.sh0)||m.sh0.length<1)throw new Error("SOGS loader: files.sh0 must have at least 1 entry (sh0)");if(!Array.isArray(m.means)||m.means.length<2)throw new Error("SOGS loader: files.means must have at least 2 entries (means_l, means_u)");if(!Array.isArray(m.quats)||m.quats.length<1)throw new Error("SOGS loader: files.quats must have at least 1 entry");if(!Array.isArray(m.scales)||m.scales.length<1)throw new Error("SOGS loader: files.scales must have at least 1 entry");const[v,S,k,B,H]=await Promise.all([M(m.means[0],"means_l"),M(m.means[1],"means_u"),M(m.quats[0],"quats"),M(m.scales[0],"scales"),M(m.sh0[0],"sh0")]),R={means_l:v,means_u:S,quats:k,scales:B,sh0:H},b=new qe(u,x,R),A=Se.packSogs(b);return b.dispose(),A}}const ft="0.3.0";G.BoundingBox=ge,G.PlyLoader=Ye,G.SogsLoader=dt,G.SplatData=Me,G.SplatMaterial=Re,G.SplatMesh=be,G.SplatSorter=Fe,G.TextureManager=Pe,G.VERSION=ft,Object.defineProperty(G,Symbol.toStringTag,{value:"Module"})});
package/dist/react.d.ts CHANGED
@@ -2,12 +2,16 @@ import type { FC } from 'react';
2
2
  import type { ThreeElements } from '@react-three/fiber';
3
3
  import type { PlyLoader, SplatMesh, SplatMeshOptions } from '@speridlabs/visus';
4
4
 
5
+ export type SplatFileType = 'ply' | 'sogs';
6
+
5
7
  /**
6
8
  * Props for the <Splat> component.
7
9
  */
8
10
  export interface SplatProps extends Omit<ThreeElements['mesh'], 'args'> {
9
- /** URL of the .ply file to load */
10
- plyUrl: string;
11
+ /** URL of the file to load (supports .ply, .sogs, .zip, .json) */
12
+ url?: string;
13
+ /** Explicit file type - required if type cannot be auto-detected from URL */
14
+ type?: SplatFileType;
11
15
  /** Options passed to the underlying SplatMesh */
12
16
  splatOptions?: SplatMeshOptions;
13
17
  debug?: boolean;