UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

1,221 lines (1,203 loc) 169 kB
import { S as ShaderStore, K as PushMaterial, aK as PrepareDefinesForMisc, aL as PrepareDefinesForFrameBoundValues, aM as PrepareDefinesForAttributes, aN as PrepareAttributesForInstances, m as VertexBuffer, aO as PrepareUniformsAndSamplersList, A as AddClipPlaneUniforms, a2 as Camera, r as BindClipPlane, X as BindFogParameters, W as BindLogDepth, Y as SerializationHelper, y as MaterialDefines, R as RegisterClass, aP as functions, aQ as HighestCommonFactor, g as Mesh, M as Matrix, V as Vector3, f as VertexData, aR as SubMesh, aS as TmpVectors, L as Logger, H as EngineStore, aT as runCoroutineAsync, aU as createYieldingScheduler, b as Tools, e as ToHalfFloat, C as Constants, aV as runCoroutineSync, a7 as Vector2, j as Color4, Q as Quaternion, aH as Color3, i as Vector4, aW as RandomRange, B as BaseTexture, aX as SPLATFileLoaderMetadata, aG as RegisterSceneLoaderPlugin } from './index-FzOfPXLV.esm.js'; import './clipPlaneFragment-OFOZXtyS.esm.js'; import './logDepthDeclaration-CD9elcsa.esm.js'; import './fogFragment-D9sBwpvW.esm.js'; import './sceneUboDeclaration-CeGVxetF.esm.js'; import './meshUboDeclaration-f8uifp9L.esm.js'; import './clipPlaneVertex-wC8bw87F.esm.js'; import './logDepthVertex-D7mEQN_F.esm.js'; import './helperFunctions-CLFdU2UC.esm.js'; import './clipPlaneFragment-Ci63y_Qd.esm.js'; import './logDepthDeclaration-LUOezDoj.esm.js'; import './fogFragment-B43lgz9g.esm.js'; import './sceneUboDeclaration-81FvQqbq.esm.js'; import './meshUboDeclaration-CjMfZXLK.esm.js'; import './helperFunctions-4InRZPbu.esm.js'; import './clipPlaneVertex-D_rrr4Jo.esm.js'; import './logDepthVertex-CnFPHy8Z.esm.js'; import { R as RawTexture } from './rawTexture-B2DimmQ5.esm.js'; import './thinInstanceMesh-BeyNtce0.esm.js'; import { A as AssetContainer } from './assetContainer-8JZnoDWQ.esm.js'; import { Ray } from './ray-D5OoQOyP.esm.js'; import { S as StandardMaterial } from './standardMaterial-DpmQ1Io2.esm.js'; // Do not edit. const name$9 = "gaussianSplattingFragmentDeclaration"; const shader$9 = `vec4 gaussianColor(vec4 inColor) {float A=-dot(vPosition,vPosition);if (A<-4.0) discard;float B=exp(A)*inColor.a; #include<logDepthFragment> vec3 color=inColor.rgb; #ifdef FOG #include<fogFragment> #endif return vec4(color,B);} `; // Sideeffect if (!ShaderStore.IncludesShadersStore[name$9]) { ShaderStore.IncludesShadersStore[name$9] = shader$9; } // Do not edit. const name$8 = "gaussianSplattingPixelShader"; const shader$8 = `#include<clipPlaneFragmentDeclaration> #include<logDepthDeclaration> #include<fogFragmentDeclaration> varying vec4 vColor;varying vec2 vPosition; #include<gaussianSplattingFragmentDeclaration> void main () { #include<clipPlaneFragment> gl_FragColor=gaussianColor(vColor);} `; // Sideeffect if (!ShaderStore.ShadersStore[name$8]) { ShaderStore.ShadersStore[name$8] = shader$8; } /** @internal */ const gaussianSplattingPixelShader = { name: name$8, shader: shader$8 }; var gaussianSplatting_fragment$1 = /*#__PURE__*/Object.freeze({ __proto__: null, gaussianSplattingPixelShader: gaussianSplattingPixelShader }); // Do not edit. const name$7 = "gaussianSplattingVertexDeclaration"; const shader$7 = `attribute vec2 position;uniform mat4 view;uniform mat4 projection;uniform mat4 world;uniform vec4 vEyePosition;`; // Sideeffect if (!ShaderStore.IncludesShadersStore[name$7]) { ShaderStore.IncludesShadersStore[name$7] = shader$7; } // Do not edit. const name$6 = "gaussianSplattingUboDeclaration"; const shader$6 = `#include<sceneUboDeclaration> #include<meshUboDeclaration> attribute vec2 position;`; // Sideeffect if (!ShaderStore.IncludesShadersStore[name$6]) { ShaderStore.IncludesShadersStore[name$6] = shader$6; } // Do not edit. const name$5 = "gaussianSplatting"; const shader$5 = `#if !defined(WEBGL2) && !defined(WEBGPU) && !defined(NATIVE) mat3 transpose(mat3 matrix) {return mat3(matrix[0][0],matrix[1][0],matrix[2][0], matrix[0][1],matrix[1][1],matrix[2][1], matrix[0][2],matrix[1][2],matrix[2][2]);} #endif vec2 getDataUV(float index,vec2 textureSize) {float y=floor(index/textureSize.x);float x=index-y*textureSize.x;return vec2((x+0.5)/textureSize.x,(y+0.5)/textureSize.y);} #if SH_DEGREE>0 ivec2 getDataUVint(float index,vec2 textureSize) {float y=floor(index/textureSize.x);float x=index-y*textureSize.x;return ivec2(uint(x+0.5),uint(y+0.5));} #endif struct Splat {vec4 center;vec4 color;vec4 covA;vec4 covB; #if SH_DEGREE>0 uvec4 sh0; #endif #if SH_DEGREE>1 uvec4 sh1; #endif #if SH_DEGREE>2 uvec4 sh2; #endif };Splat readSplat(float splatIndex) {Splat splat;vec2 splatUV=getDataUV(splatIndex,dataTextureSize);splat.center=texture2D(centersTexture,splatUV);splat.color=texture2D(colorsTexture,splatUV);splat.covA=texture2D(covariancesATexture,splatUV)*splat.center.w;splat.covB=texture2D(covariancesBTexture,splatUV)*splat.center.w; #if SH_DEGREE>0 ivec2 splatUVint=getDataUVint(splatIndex,dataTextureSize);splat.sh0=texelFetch(shTexture0,splatUVint,0); #endif #if SH_DEGREE>1 splat.sh1=texelFetch(shTexture1,splatUVint,0); #endif #if SH_DEGREE>2 splat.sh2=texelFetch(shTexture2,splatUVint,0); #endif return splat;} #if defined(WEBGL2) || defined(WEBGPU) || defined(NATIVE) vec3 computeColorFromSHDegree(vec3 dir,const vec3 sh[16]) {const float SH_C0=0.28209479;const float SH_C1=0.48860251;float SH_C2[5];SH_C2[0]=1.092548430;SH_C2[1]=-1.09254843;SH_C2[2]=0.315391565;SH_C2[3]=-1.09254843;SH_C2[4]=0.546274215;float SH_C3[7];SH_C3[0]=-0.59004358;SH_C3[1]=2.890611442;SH_C3[2]=-0.45704579;SH_C3[3]=0.373176332;SH_C3[4]=-0.45704579;SH_C3[5]=1.445305721;SH_C3[6]=-0.59004358;vec3 result=/*SH_C0**/sh[0]; #if SH_DEGREE>0 float x=dir.x;float y=dir.y;float z=dir.z;result+=- SH_C1*y*sh[1]+SH_C1*z*sh[2]-SH_C1*x*sh[3]; #if SH_DEGREE>1 float xx=x*x,yy=y*y,zz=z*z;float xy=x*y,yz=y*z,xz=x*z;result+= SH_C2[0]*xy*sh[4] + SH_C2[1]*yz*sh[5] + SH_C2[2]*(2.0*zz-xx-yy)*sh[6] + SH_C2[3]*xz*sh[7] + SH_C2[4]*(xx-yy)*sh[8]; #if SH_DEGREE>2 result+= SH_C3[0]*y*(3.0*xx-yy)*sh[9] + SH_C3[1]*xy*z*sh[10] + SH_C3[2]*y*(4.0*zz-xx-yy)*sh[11] + SH_C3[3]*z*(2.0*zz-3.0*xx-3.0*yy)*sh[12] + SH_C3[4]*x*(4.0*zz-xx-yy)*sh[13] + SH_C3[5]*z*(xx-yy)*sh[14] + SH_C3[6]*x*(xx-3.0*yy)*sh[15]; #endif #endif #endif return result;} vec4 decompose(uint value) {vec4 components=vec4( float((value ) & 255u), float((value>>uint( 8)) & 255u), float((value>>uint(16)) & 255u), float((value>>uint(24)) & 255u));return components*vec4(2./255.)-vec4(1.);} vec3 computeSH(Splat splat,vec3 dir) {vec3 sh[16];sh[0]=vec3(0.,0.,0.); #if SH_DEGREE>0 vec4 sh00=decompose(splat.sh0.x);vec4 sh01=decompose(splat.sh0.y);vec4 sh02=decompose(splat.sh0.z);sh[1]=vec3(sh00.x,sh00.y,sh00.z);sh[2]=vec3(sh00.w,sh01.x,sh01.y);sh[3]=vec3(sh01.z,sh01.w,sh02.x); #endif #if SH_DEGREE>1 vec4 sh03=decompose(splat.sh0.w);vec4 sh04=decompose(splat.sh1.x);vec4 sh05=decompose(splat.sh1.y);sh[4]=vec3(sh02.y,sh02.z,sh02.w);sh[5]=vec3(sh03.x,sh03.y,sh03.z);sh[6]=vec3(sh03.w,sh04.x,sh04.y);sh[7]=vec3(sh04.z,sh04.w,sh05.x);sh[8]=vec3(sh05.y,sh05.z,sh05.w); #endif #if SH_DEGREE>2 vec4 sh06=decompose(splat.sh1.z);vec4 sh07=decompose(splat.sh1.w);vec4 sh08=decompose(splat.sh2.x);vec4 sh09=decompose(splat.sh2.y);vec4 sh10=decompose(splat.sh2.z);vec4 sh11=decompose(splat.sh2.w);sh[9]=vec3(sh06.x,sh06.y,sh06.z);sh[10]=vec3(sh06.w,sh07.x,sh07.y);sh[11]=vec3(sh07.z,sh07.w,sh08.x);sh[12]=vec3(sh08.y,sh08.z,sh08.w);sh[13]=vec3(sh09.x,sh09.y,sh09.z);sh[14]=vec3(sh09.w,sh10.x,sh10.y);sh[15]=vec3(sh10.z,sh10.w,sh11.x); #endif return computeColorFromSHDegree(dir,sh);} #else vec3 computeSH(Splat splat,vec3 dir) {return vec3(0.,0.,0.);} #endif vec4 gaussianSplatting(vec2 meshPos,vec3 worldPos,vec2 scale,vec3 covA,vec3 covB,mat4 worldMatrix,mat4 viewMatrix,mat4 projectionMatrix) {mat4 modelView=viewMatrix*worldMatrix;vec4 camspace=viewMatrix*vec4(worldPos,1.);vec4 pos2d=projectionMatrix*camspace;float bounds=1.2*pos2d.w;if (pos2d.z<-pos2d.w || pos2d.x<-bounds || pos2d.x>bounds || pos2d.y<-bounds || pos2d.y>bounds) {return vec4(0.0,0.0,2.0,1.0);} mat3 Vrk=mat3( covA.x,covA.y,covA.z, covA.y,covB.x,covB.y, covA.z,covB.y,covB.z );mat3 J=mat3( focal.x/camspace.z,0.,-(focal.x*camspace.x)/(camspace.z*camspace.z), 0.,focal.y/camspace.z,-(focal.y*camspace.y)/(camspace.z*camspace.z), 0.,0.,0. );mat3 invy=mat3(1,0,0,0,-1,0,0,0,1);mat3 T=invy*transpose(mat3(modelView))*J;mat3 cov2d=transpose(T)*Vrk*T; #if COMPENSATION float c00=cov2d[0][0];float c11=cov2d[1][1];float c01=cov2d[0][1];float detOrig=c00*c11-c01*c01; #endif cov2d[0][0]+=kernelSize;cov2d[1][1]+=kernelSize; #if COMPENSATION vec3 c2d=vec3(cov2d[0][0],c01,cov2d[1][1]);float detBlur=c2d.x*c2d.z-c2d.y*c2d.y;float compensation=sqrt(max(0.,detOrig/detBlur));vColor.w*=compensation; #endif float mid=(cov2d[0][0]+cov2d[1][1])/2.0;float radius=length(vec2((cov2d[0][0]-cov2d[1][1])/2.0,cov2d[0][1]));float epsilon=0.0001;float lambda1=mid+radius+epsilon,lambda2=mid-radius+epsilon;if (lambda2<0.0) {return vec4(0.0,0.0,2.0,1.0);} vec2 diagonalVector=normalize(vec2(cov2d[0][1],lambda1-cov2d[0][0]));vec2 majorAxis=min(sqrt(2.0*lambda1),1024.0)*diagonalVector;vec2 minorAxis=min(sqrt(2.0*lambda2),1024.0)*vec2(diagonalVector.y,-diagonalVector.x);vec2 vCenter=vec2(pos2d);return vec4( vCenter + ((meshPos.x*majorAxis + meshPos.y*minorAxis)*invViewport*pos2d.w)*scale,pos2d.zw);}`; // Sideeffect if (!ShaderStore.IncludesShadersStore[name$5]) { ShaderStore.IncludesShadersStore[name$5] = shader$5; } // Do not edit. const name$4 = "gaussianSplattingVertexShader"; const shader$4 = `#include<__decl__gaussianSplattingVertex> #ifdef LOGARITHMICDEPTH #extension GL_EXT_frag_depth : enable #endif #include<clipPlaneVertexDeclaration> #include<fogVertexDeclaration> #include<logDepthDeclaration> #include<helperFunctions> attribute float splatIndex;uniform vec2 invViewport;uniform vec2 dataTextureSize;uniform vec2 focal;uniform float kernelSize;uniform vec3 eyePosition;uniform vec3 viewDirectionFactor;uniform sampler2D covariancesATexture;uniform sampler2D covariancesBTexture;uniform sampler2D centersTexture;uniform sampler2D colorsTexture; #if SH_DEGREE>0 uniform highp usampler2D shTexture0; #endif #if SH_DEGREE>1 uniform highp usampler2D shTexture1; #endif #if SH_DEGREE>2 uniform highp usampler2D shTexture2; #endif varying vec4 vColor;varying vec2 vPosition; #include<gaussianSplatting> void main () {Splat splat=readSplat(splatIndex);vec3 covA=splat.covA.xyz;vec3 covB=vec3(splat.covA.w,splat.covB.xy);vec4 worldPos=world*vec4(splat.center.xyz,1.0);vColor=splat.color;vPosition=position; #if SH_DEGREE>0 mat3 worldRot=mat3(world);mat3 normWorldRot=inverseMat3(worldRot);vec3 dir=normalize(normWorldRot*(worldPos.xyz-eyePosition));dir*=viewDirectionFactor;vColor.xyz=splat.color.xyz+computeSH(splat,dir); #endif gl_Position=gaussianSplatting(position,worldPos.xyz,vec2(1.,1.),covA,covB,world,view,projection); #include<clipPlaneVertex> #include<fogVertex> #include<logDepthVertex> } `; // Sideeffect if (!ShaderStore.ShadersStore[name$4]) { ShaderStore.ShadersStore[name$4] = shader$4; } /** @internal */ const gaussianSplattingVertexShader = { name: name$4, shader: shader$4 }; var gaussianSplatting_vertex$1 = /*#__PURE__*/Object.freeze({ __proto__: null, gaussianSplattingVertexShader: gaussianSplattingVertexShader }); // Do not edit. const name$3 = "gaussianSplattingFragmentDeclaration"; const shader$3 = `fn gaussianColor(inColor: vec4f,inPosition: vec2f)->vec4f {var A : f32=-dot(inPosition,inPosition);if (A>-4.0) {var B: f32=exp(A)*inColor.a; #include<logDepthFragment> var color: vec3f=inColor.rgb; #ifdef FOG #include<fogFragment> #endif return vec4f(color,B);} else {return vec4f(0.0);}} `; // Sideeffect if (!ShaderStore.IncludesShadersStoreWGSL[name$3]) { ShaderStore.IncludesShadersStoreWGSL[name$3] = shader$3; } // Do not edit. const name$2 = "gaussianSplattingPixelShader"; const shader$2 = `#include<clipPlaneFragmentDeclaration> #include<logDepthDeclaration> #include<fogFragmentDeclaration> varying vColor: vec4f;varying vPosition: vec2f; #include<gaussianSplattingFragmentDeclaration> @fragment fn main(input: FragmentInputs)->FragmentOutputs { #include<clipPlaneFragment> fragmentOutputs.color=gaussianColor(input.vColor,input.vPosition);} `; // Sideeffect if (!ShaderStore.ShadersStoreWGSL[name$2]) { ShaderStore.ShadersStoreWGSL[name$2] = shader$2; } /** @internal */ const gaussianSplattingPixelShaderWGSL = { name: name$2, shader: shader$2 }; var gaussianSplatting_fragment = /*#__PURE__*/Object.freeze({ __proto__: null, gaussianSplattingPixelShaderWGSL: gaussianSplattingPixelShaderWGSL }); // Do not edit. const name$1 = "gaussianSplatting"; const shader$1 = `fn getDataUV(index: f32,dataTextureSize: vec2f)->vec2<f32> {let y: f32=floor(index/dataTextureSize.x);let x: f32=index-y*dataTextureSize.x;return vec2f((x+0.5),(y+0.5));} struct Splat {center: vec4f, color: vec4f, covA: vec4f, covB: vec4f, #if SH_DEGREE>0 sh0: vec4<u32>, #endif #if SH_DEGREE>1 sh1: vec4<u32>, #endif #if SH_DEGREE>2 sh2: vec4<u32>, #endif };fn readSplat(splatIndex: f32,dataTextureSize: vec2f)->Splat {var splat: Splat;let splatUV=getDataUV(splatIndex,dataTextureSize);let splatUVi32=vec2<i32>(i32(splatUV.x),i32(splatUV.y));splat.center=textureLoad(centersTexture,splatUVi32,0);splat.color=textureLoad(colorsTexture,splatUVi32,0);splat.covA=textureLoad(covariancesATexture,splatUVi32,0)*splat.center.w;splat.covB=textureLoad(covariancesBTexture,splatUVi32,0)*splat.center.w; #if SH_DEGREE>0 splat.sh0=textureLoad(shTexture0,splatUVi32,0); #endif #if SH_DEGREE>1 splat.sh1=textureLoad(shTexture1,splatUVi32,0); #endif #if SH_DEGREE>2 splat.sh2=textureLoad(shTexture2,splatUVi32,0); #endif return splat;} fn computeColorFromSHDegree(dir: vec3f,sh: array<vec3<f32>,16>)->vec3f {let SH_C0: f32=0.28209479;let SH_C1: f32=0.48860251;var SH_C2: array<f32,5>=array<f32,5>( 1.092548430, -1.09254843, 0.315391565, -1.09254843, 0.546274215 );var SH_C3: array<f32,7>=array<f32,7>( -0.59004358, 2.890611442, -0.45704579, 0.373176332, -0.45704579, 1.445305721, -0.59004358 );var result: vec3f=/*SH_C0**/sh[0]; #if SH_DEGREE>0 let x: f32=dir.x;let y: f32=dir.y;let z: f32=dir.z;result+=-SH_C1*y*sh[1]+SH_C1*z*sh[2]-SH_C1*x*sh[3]; #if SH_DEGREE>1 let xx: f32=x*x;let yy: f32=y*y;let zz: f32=z*z;let xy: f32=x*y;let yz: f32=y*z;let xz: f32=x*z;result+= SH_C2[0]*xy*sh[4] + SH_C2[1]*yz*sh[5] + SH_C2[2]*(2.0f*zz-xx-yy)*sh[6] + SH_C2[3]*xz*sh[7] + SH_C2[4]*(xx-yy)*sh[8]; #if SH_DEGREE>2 result+= SH_C3[0]*y*(3.0f*xx-yy)*sh[9] + SH_C3[1]*xy*z*sh[10] + SH_C3[2]*y*(4.0f*zz-xx-yy)*sh[11] + SH_C3[3]*z*(2.0f*zz-3.0f*xx-3.0f*yy)*sh[12] + SH_C3[4]*x*(4.0f*zz-xx-yy)*sh[13] + SH_C3[5]*z*(xx-yy)*sh[14] + SH_C3[6]*x*(xx-3.0f*yy)*sh[15]; #endif #endif #endif return result;} fn decompose(value: u32)->vec4f {let components : vec4f=vec4f( f32((value ) & 255u), f32((value>>u32( 8)) & 255u), f32((value>>u32(16)) & 255u), f32((value>>u32(24)) & 255u));return components*vec4f(2./255.)-vec4f(1.);} fn computeSH(splat: Splat,dir: vec3f)->vec3f {var sh: array<vec3<f32>,16>;sh[0]=vec3f(0.,0.,0.); #if SH_DEGREE>0 let sh00: vec4f=decompose(splat.sh0.x);let sh01: vec4f=decompose(splat.sh0.y);let sh02: vec4f=decompose(splat.sh0.z);sh[1]=vec3f(sh00.x,sh00.y,sh00.z);sh[2]=vec3f(sh00.w,sh01.x,sh01.y);sh[3]=vec3f(sh01.z,sh01.w,sh02.x); #endif #if SH_DEGREE>1 let sh03: vec4f=decompose(splat.sh0.w);let sh04: vec4f=decompose(splat.sh1.x);let sh05: vec4f=decompose(splat.sh1.y);sh[4]=vec3f(sh02.y,sh02.z,sh02.w);sh[5]=vec3f(sh03.x,sh03.y,sh03.z);sh[6]=vec3f(sh03.w,sh04.x,sh04.y);sh[7]=vec3f(sh04.z,sh04.w,sh05.x);sh[8]=vec3f(sh05.y,sh05.z,sh05.w); #endif #if SH_DEGREE>2 let sh06: vec4f=decompose(splat.sh1.z);let sh07: vec4f=decompose(splat.sh1.w);let sh08: vec4f=decompose(splat.sh2.x);let sh09: vec4f=decompose(splat.sh2.y);let sh10: vec4f=decompose(splat.sh2.z);let sh11: vec4f=decompose(splat.sh2.w);sh[9]=vec3f(sh06.x,sh06.y,sh06.z);sh[10]=vec3f(sh06.w,sh07.x,sh07.y);sh[11]=vec3f(sh07.z,sh07.w,sh08.x);sh[12]=vec3f(sh08.y,sh08.z,sh08.w);sh[13]=vec3f(sh09.x,sh09.y,sh09.z);sh[14]=vec3f(sh09.w,sh10.x,sh10.y);sh[15]=vec3f(sh10.z,sh10.w,sh11.x); #endif return computeColorFromSHDegree(dir,sh);} fn gaussianSplatting( meshPos: vec2<f32>, worldPos: vec3<f32>, scale: vec2<f32>, covA: vec3<f32>, covB: vec3<f32>, worldMatrix: mat4x4<f32>, viewMatrix: mat4x4<f32>, projectionMatrix: mat4x4<f32>, focal: vec2f, invViewport: vec2f, kernelSize: f32 )->vec4f {let modelView=viewMatrix*worldMatrix;let camspace=viewMatrix*vec4f(worldPos,1.0);let pos2d=projectionMatrix*camspace;let bounds=1.2*pos2d.w;if (pos2d.z<0. || pos2d.x<-bounds || pos2d.x>bounds || pos2d.y<-bounds || pos2d.y>bounds) {return vec4f(0.0,0.0,2.0,1.0);} let Vrk=mat3x3<f32>( covA.x,covA.y,covA.z, covA.y,covB.x,covB.y, covA.z,covB.y,covB.z );let J=mat3x3<f32>( focal.x/camspace.z,0.0,-(focal.x*camspace.x)/(camspace.z*camspace.z), 0.0,focal.y/camspace.z,-(focal.y*camspace.y)/(camspace.z*camspace.z), 0.0,0.0,0.0 );let invy=mat3x3<f32>( 1.0,0.0,0.0, 0.0,-1.0,0.0, 0.0,0.0,1.0 );let T=invy*transpose(mat3x3<f32>( modelView[0].xyz, modelView[1].xyz, modelView[2].xyz))*J;var cov2d=transpose(T)*Vrk*T; #if COMPENSATION let c00: f32=cov2d[0][0];let c11: f32=cov2d[1][1];let c01: f32=cov2d[0][1];let detOrig: f32=c00*c11-c01*c01; #endif cov2d[0][0]+=kernelSize;cov2d[1][1]+=kernelSize; #if COMPENSATION let c2d: vec3f=vec3f(cov2d[0][0],c01,cov2d[1][1]);let detBlur: f32=c2d.x*c2d.z-c2d.y*c2d.y;let compensation: f32=sqrt(max(0.,detOrig/detBlur));vertexOutputs.vColor.w*=compensation; #endif let mid=(cov2d[0][0]+cov2d[1][1])/2.0;let radius=length(vec2<f32>((cov2d[0][0]-cov2d[1][1])/2.0,cov2d[0][1]));let lambda1=mid+radius;let lambda2=mid-radius;if (lambda2<0.0) {return vec4f(0.0,0.0,2.0,1.0);} let diagonalVector=normalize(vec2<f32>(cov2d[0][1],lambda1-cov2d[0][0]));let majorAxis=min(sqrt(2.0*lambda1),1024.0)*diagonalVector;let minorAxis=min(sqrt(2.0*lambda2),1024.0)*vec2<f32>(diagonalVector.y,-diagonalVector.x);let vCenter=vec2<f32>(pos2d.x,pos2d.y);return vec4f( vCenter+((meshPos.x*majorAxis+meshPos.y*minorAxis)*invViewport*pos2d.w)*scale, pos2d.z, pos2d.w );} `; // Sideeffect if (!ShaderStore.IncludesShadersStoreWGSL[name$1]) { ShaderStore.IncludesShadersStoreWGSL[name$1] = shader$1; } // Do not edit. const name = "gaussianSplattingVertexShader"; const shader = `#include<sceneUboDeclaration> #include<meshUboDeclaration> #include<helperFunctions> #include<clipPlaneVertexDeclaration> #include<fogVertexDeclaration> #include<logDepthDeclaration> attribute splatIndex: f32;attribute position: vec2f;uniform invViewport: vec2f;uniform dataTextureSize: vec2f;uniform focal: vec2f;uniform kernelSize: f32;uniform eyePosition: vec3f;uniform viewDirectionFactor: vec3f;var covariancesATexture: texture_2d<f32>;var covariancesBTexture: texture_2d<f32>;var centersTexture: texture_2d<f32>;var colorsTexture: texture_2d<f32>; #if SH_DEGREE>0 var shTexture0: texture_2d<u32>; #endif #if SH_DEGREE>1 var shTexture1: texture_2d<u32>; #endif #if SH_DEGREE>2 var shTexture2: texture_2d<u32>; #endif varying vColor: vec4f;varying vPosition: vec2f; #include<gaussianSplatting> @vertex fn main(input : VertexInputs)->FragmentInputs {var splat: Splat=readSplat(input.splatIndex,uniforms.dataTextureSize);var covA: vec3f=splat.covA.xyz;var covB: vec3f=vec3f(splat.covA.w,splat.covB.xy);let worldPos: vec4f=mesh.world*vec4f(splat.center.xyz,1.0);vertexOutputs.vPosition=input.position; #if SH_DEGREE>0 let worldRot: mat3x3f= mat3x3f(mesh.world[0].xyz,mesh.world[1].xyz,mesh.world[2].xyz);let normWorldRot: mat3x3f=inverseMat3(worldRot);var dir: vec3f=normalize(normWorldRot*(worldPos.xyz-uniforms.eyePosition.xyz));dir*=viewDirectionFactor;vertexOutputs.vColor=vec4f(splat.color.xyz+computeSH(splat,dir),splat.color.w); #else vertexOutputs.vColor=splat.color; #endif vertexOutputs.position=gaussianSplatting(input.position,worldPos.xyz,vec2f(1.0,1.0),covA,covB,mesh.world,scene.view,scene.projection,uniforms.focal,uniforms.invViewport,uniforms.kernelSize); #include<clipPlaneVertex> #include<fogVertex> #include<logDepthVertex> } `; // Sideeffect if (!ShaderStore.ShadersStoreWGSL[name]) { ShaderStore.ShadersStoreWGSL[name] = shader; } /** @internal */ const gaussianSplattingVertexShaderWGSL = { name, shader }; var gaussianSplatting_vertex = /*#__PURE__*/Object.freeze({ __proto__: null, gaussianSplattingVertexShaderWGSL: gaussianSplattingVertexShaderWGSL }); /** * @internal */ class GaussianSplattingMaterialDefines extends MaterialDefines { /** * Constructor of the defines. */ constructor() { super(); this.FOG = false; this.THIN_INSTANCES = true; this.LOGARITHMICDEPTH = false; this.CLIPPLANE = false; this.CLIPPLANE2 = false; this.CLIPPLANE3 = false; this.CLIPPLANE4 = false; this.CLIPPLANE5 = false; this.CLIPPLANE6 = false; this.SH_DEGREE = 0; this.COMPENSATION = false; this.rebuild(); } } /** * GaussianSplattingMaterial material used to render Gaussian Splatting * @experimental */ class GaussianSplattingMaterial extends PushMaterial { /** * Instantiates a Gaussian Splatting Material in the given scene * @param name The friendly name of the material * @param scene The scene to add the material to */ constructor(name, scene) { super(name, scene); /** * Point spread function (default 0.3). Can be overriden per GS material, otherwise, using default static `KernelSize` value */ this.kernelSize = GaussianSplattingMaterial.KernelSize; this._compensation = GaussianSplattingMaterial.Compensation; // set to true when material defines are dirty this._isDirty = false; this.backFaceCulling = false; } /** * Set compensation default value is `GaussianSplattingMaterial.Compensation` */ set compensation(value) { this._isDirty = this._isDirty != value; this._compensation = value; } /** * Get compensation */ get compensation() { return this._compensation; } /** * Gets a boolean indicating that current material needs to register RTT */ get hasRenderTargetTextures() { return false; } /** * Specifies whether or not this material should be rendered in alpha test mode. * @returns false */ needAlphaTesting() { return false; } /** * Specifies whether or not this material should be rendered in alpha blend mode. * @returns true */ needAlphaBlending() { return true; } /** * Checks whether the material is ready to be rendered for a given mesh. * @param mesh The mesh to render * @param subMesh The submesh to check against * @returns true if all the dependencies are ready (Textures, Effects...) */ isReadyForSubMesh(mesh, subMesh) { const useInstances = true; const drawWrapper = subMesh._drawWrapper; let defines = subMesh.materialDefines; if (defines && this._isDirty) { defines.markAsUnprocessed(); } if (drawWrapper.effect && this.isFrozen) { if (drawWrapper._wasPreviouslyReady && drawWrapper._wasPreviouslyUsingInstances === useInstances) { return true; } } if (!subMesh.materialDefines) { defines = subMesh.materialDefines = new GaussianSplattingMaterialDefines(); } const scene = this.getScene(); if (this._isReadyForSubMesh(subMesh)) { return true; } const engine = scene.getEngine(); const gsMesh = mesh; // Misc. PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, false, defines); // Values that need to be evaluated on every frame PrepareDefinesForFrameBoundValues(scene, engine, this, defines, useInstances, null, true); // Attribs PrepareDefinesForAttributes(mesh, defines, false, false); // SH is disabled for webGL1 if (engine.version > 1 || engine.isWebGPU) { defines["SH_DEGREE"] = gsMesh.shDegree; } // Compensation const splatMaterial = gsMesh.material; defines["COMPENSATION"] = splatMaterial && splatMaterial.compensation ? splatMaterial.compensation : GaussianSplattingMaterial.Compensation; // Get correct effect if (defines.isDirty) { defines.markAsProcessed(); scene.resetCachedMaterial(); //Attributes const attribs = [VertexBuffer.PositionKind, "splatIndex"]; PrepareAttributesForInstances(attribs, defines); const uniforms = [ "world", "view", "projection", "vFogInfos", "vFogColor", "logarithmicDepthConstant", "invViewport", "dataTextureSize", "focal", "eyePosition", "kernelSize", "viewDirectionFactor", ]; const samplers = ["covariancesATexture", "covariancesBTexture", "centersTexture", "colorsTexture", "shTexture0", "shTexture1", "shTexture2"]; const uniformBuffers = ["Scene", "Mesh"]; PrepareUniformsAndSamplersList({ uniformsNames: uniforms, uniformBuffersNames: uniformBuffers, samplers: samplers, defines: defines, }); AddClipPlaneUniforms(uniforms); const join = defines.toString(); const effect = scene.getEngine().createEffect("gaussianSplatting", { attributes: attribs, uniformsNames: uniforms, uniformBuffersNames: uniformBuffers, samplers: samplers, defines: join, onCompiled: this.onCompiled, onError: this.onError, indexParameters: {}, shaderLanguage: this._shaderLanguage, extraInitializationsAsync: async () => { if (this._shaderLanguage === 1 /* ShaderLanguage.WGSL */) { await Promise.all([Promise.resolve().then(function () { return gaussianSplatting_fragment; }), Promise.resolve().then(function () { return gaussianSplatting_vertex; })]); } else { await Promise.all([Promise.resolve().then(function () { return gaussianSplatting_fragment$1; }), Promise.resolve().then(function () { return gaussianSplatting_vertex$1; })]); } }, }, engine); subMesh.setEffect(effect, defines, this._materialContext); } if (!subMesh.effect || !subMesh.effect.isReady()) { return false; } defines._renderId = scene.getRenderId(); drawWrapper._wasPreviouslyReady = true; drawWrapper._wasPreviouslyUsingInstances = useInstances; this._isDirty = false; return true; } /** * Bind material effect for a specific Gaussian Splatting mesh * @param mesh Gaussian splatting mesh * @param effect Splatting material or node material * @param scene scene that contains mesh and camera used for rendering */ static BindEffect(mesh, effect, scene) { const engine = scene.getEngine(); const camera = scene.activeCamera; const renderWidth = engine.getRenderWidth(); const renderHeight = engine.getRenderHeight(); const gsMesh = mesh; const gsMaterial = gsMesh.material; // check if rigcamera, get number of rigs const numberOfRigs = camera?.rigParent?.rigCameras.length || 1; effect.setFloat2("invViewport", 1 / (renderWidth / numberOfRigs), 1 / renderHeight); let focal = 1000; if (camera) { /* more explicit version: const t = camera.getProjectionMatrix().m[5]; const FovY = Math.atan(1.0 / t) * 2.0; focal = renderHeight / 2.0 / Math.tan(FovY / 2.0); Using a shorter version here to not have tan(atan) and 2.0 factor */ const t = camera.getProjectionMatrix().m[5]; if (camera.fovMode == Camera.FOVMODE_VERTICAL_FIXED) { focal = (renderHeight * t) / 2.0; } else { focal = (renderWidth * t) / 2.0; } } effect.setFloat2("focal", focal, focal); effect.setVector3("viewDirectionFactor", gsMesh.viewDirectionFactor); effect.setFloat("kernelSize", gsMaterial && gsMaterial.kernelSize ? gsMaterial.kernelSize : GaussianSplattingMaterial.KernelSize); scene.bindEyePosition(effect, "eyePosition", true); if (gsMesh.covariancesATexture) { const textureSize = gsMesh.covariancesATexture.getSize(); effect.setFloat2("dataTextureSize", textureSize.width, textureSize.height); effect.setTexture("covariancesATexture", gsMesh.covariancesATexture); effect.setTexture("covariancesBTexture", gsMesh.covariancesBTexture); effect.setTexture("centersTexture", gsMesh.centersTexture); effect.setTexture("colorsTexture", gsMesh.colorsTexture); if (gsMesh.shTextures) { for (let i = 0; i < gsMesh.shTextures?.length; i++) { effect.setTexture(`shTexture${i}`, gsMesh.shTextures[i]); } } } } /** * Binds the submesh to this material by preparing the effect and shader to draw * @param world defines the world transformation matrix * @param mesh defines the mesh containing the submesh * @param subMesh defines the submesh to bind the material to */ bindForSubMesh(world, mesh, subMesh) { const scene = this.getScene(); const defines = subMesh.materialDefines; if (!defines) { return; } const effect = subMesh.effect; if (!effect) { return; } this._activeEffect = effect; // Matrices Mesh. mesh.getMeshUniformBuffer().bindToEffect(effect, "Mesh"); mesh.transferToEffect(world); // Bind data const mustRebind = this._mustRebind(scene, effect, subMesh, mesh.visibility); if (mustRebind) { this.bindView(effect); this.bindViewProjection(effect); GaussianSplattingMaterial.BindEffect(mesh, this._activeEffect, scene); // Clip plane BindClipPlane(effect, this, scene); } else if (scene.getEngine()._features.needToAlwaysBindUniformBuffers) { this._needToBindSceneUbo = true; } // Fog BindFogParameters(scene, mesh, effect); // Log. depth if (this.useLogarithmicDepth) { BindLogDepth(defines, effect, scene); } this._afterBind(mesh, this._activeEffect, subMesh); } /** * Clones the material. * @param name The cloned name. * @returns The cloned material. */ clone(name) { return SerializationHelper.Clone(() => new GaussianSplattingMaterial(name, this.getScene()), this); } /** * Serializes the current material to its JSON representation. * @returns The JSON representation. */ serialize() { const serializationObject = super.serialize(); serializationObject.customType = "BABYLON.GaussianSplattingMaterial"; return serializationObject; } /** * Gets the class name of the material * @returns "GaussianSplattingMaterial" */ getClassName() { return "GaussianSplattingMaterial"; } /** * Parse a JSON input to create back a Gaussian Splatting material. * @param source The JSON data to parse * @param scene The scene to create the parsed material in * @param rootUrl The root url of the assets the material depends upon * @returns the instantiated GaussianSplattingMaterial. */ static Parse(source, scene, rootUrl) { return SerializationHelper.Parse(() => new GaussianSplattingMaterial(source.name, scene), source, scene, rootUrl); } } /** * Point spread function (default 0.3). Can be overriden per GS material */ GaussianSplattingMaterial.KernelSize = 0.3; /** * Compensation */ GaussianSplattingMaterial.Compensation = false; RegisterClass("BABYLON.GaussianSplattingMaterial", GaussianSplattingMaterial); /* eslint-disable @typescript-eslint/naming-convention */ const HCF = HighestCommonFactor; /** * Scalar computation library */ const Scalar = { ...functions, /** * Two pi constants convenient for computation. */ TwoPi: Math.PI * 2, /** * Returns -1 if value is negative and +1 is value is positive. * @param value the value * @returns the value itself if it's equal to zero. */ Sign: Math.sign, /** * the log2 of value. * @param value the value to compute log2 of * @returns the log2 of value. */ Log2: Math.log2, /** * Returns the highest common factor of two integers. * @param a first parameter * @param b second parameter * @returns HCF of a and b */ HCF, }; /* eslint-enable @typescript-eslint/naming-convention */ // @internal const UnpackUnorm = (value, bits) => { const t = (1 << bits) - 1; return (value & t) / t; }; // @internal const Unpack111011 = (value, result) => { result.x = UnpackUnorm(value >>> 21, 11); result.y = UnpackUnorm(value >>> 11, 10); result.z = UnpackUnorm(value, 11); }; // @internal const Unpack8888 = (value, result) => { result[0] = UnpackUnorm(value >>> 24, 8) * 255; result[1] = UnpackUnorm(value >>> 16, 8) * 255; result[2] = UnpackUnorm(value >>> 8, 8) * 255; result[3] = UnpackUnorm(value, 8) * 255; }; // @internal // unpack quaternion with 2,10,10,10 format (largest element, 3x10bit element) const UnpackRot = (value, result) => { const norm = 1.0 / (Math.sqrt(2) * 0.5); const a = (UnpackUnorm(value >>> 20, 10) - 0.5) * norm; const b = (UnpackUnorm(value >>> 10, 10) - 0.5) * norm; const c = (UnpackUnorm(value, 10) - 0.5) * norm; const m = Math.sqrt(1.0 - (a * a + b * b + c * c)); switch (value >>> 30) { case 0: result.set(m, a, b, c); break; case 1: result.set(a, m, b, c); break; case 2: result.set(a, b, m, c); break; case 3: result.set(a, b, c, m); break; } }; /** * Representation of the types */ var PLYType; (function (PLYType) { PLYType[PLYType["FLOAT"] = 0] = "FLOAT"; PLYType[PLYType["INT"] = 1] = "INT"; PLYType[PLYType["UINT"] = 2] = "UINT"; PLYType[PLYType["DOUBLE"] = 3] = "DOUBLE"; PLYType[PLYType["UCHAR"] = 4] = "UCHAR"; PLYType[PLYType["UNDEFINED"] = 5] = "UNDEFINED"; })(PLYType || (PLYType = {})); /** * Usage types of the PLY values */ var PLYValue; (function (PLYValue) { PLYValue[PLYValue["MIN_X"] = 0] = "MIN_X"; PLYValue[PLYValue["MIN_Y"] = 1] = "MIN_Y"; PLYValue[PLYValue["MIN_Z"] = 2] = "MIN_Z"; PLYValue[PLYValue["MAX_X"] = 3] = "MAX_X"; PLYValue[PLYValue["MAX_Y"] = 4] = "MAX_Y"; PLYValue[PLYValue["MAX_Z"] = 5] = "MAX_Z"; PLYValue[PLYValue["MIN_SCALE_X"] = 6] = "MIN_SCALE_X"; PLYValue[PLYValue["MIN_SCALE_Y"] = 7] = "MIN_SCALE_Y"; PLYValue[PLYValue["MIN_SCALE_Z"] = 8] = "MIN_SCALE_Z"; PLYValue[PLYValue["MAX_SCALE_X"] = 9] = "MAX_SCALE_X"; PLYValue[PLYValue["MAX_SCALE_Y"] = 10] = "MAX_SCALE_Y"; PLYValue[PLYValue["MAX_SCALE_Z"] = 11] = "MAX_SCALE_Z"; PLYValue[PLYValue["PACKED_POSITION"] = 12] = "PACKED_POSITION"; PLYValue[PLYValue["PACKED_ROTATION"] = 13] = "PACKED_ROTATION"; PLYValue[PLYValue["PACKED_SCALE"] = 14] = "PACKED_SCALE"; PLYValue[PLYValue["PACKED_COLOR"] = 15] = "PACKED_COLOR"; PLYValue[PLYValue["X"] = 16] = "X"; PLYValue[PLYValue["Y"] = 17] = "Y"; PLYValue[PLYValue["Z"] = 18] = "Z"; PLYValue[PLYValue["SCALE_0"] = 19] = "SCALE_0"; PLYValue[PLYValue["SCALE_1"] = 20] = "SCALE_1"; PLYValue[PLYValue["SCALE_2"] = 21] = "SCALE_2"; PLYValue[PLYValue["DIFFUSE_RED"] = 22] = "DIFFUSE_RED"; PLYValue[PLYValue["DIFFUSE_GREEN"] = 23] = "DIFFUSE_GREEN"; PLYValue[PLYValue["DIFFUSE_BLUE"] = 24] = "DIFFUSE_BLUE"; PLYValue[PLYValue["OPACITY"] = 25] = "OPACITY"; PLYValue[PLYValue["F_DC_0"] = 26] = "F_DC_0"; PLYValue[PLYValue["F_DC_1"] = 27] = "F_DC_1"; PLYValue[PLYValue["F_DC_2"] = 28] = "F_DC_2"; PLYValue[PLYValue["F_DC_3"] = 29] = "F_DC_3"; PLYValue[PLYValue["ROT_0"] = 30] = "ROT_0"; PLYValue[PLYValue["ROT_1"] = 31] = "ROT_1"; PLYValue[PLYValue["ROT_2"] = 32] = "ROT_2"; PLYValue[PLYValue["ROT_3"] = 33] = "ROT_3"; PLYValue[PLYValue["MIN_COLOR_R"] = 34] = "MIN_COLOR_R"; PLYValue[PLYValue["MIN_COLOR_G"] = 35] = "MIN_COLOR_G"; PLYValue[PLYValue["MIN_COLOR_B"] = 36] = "MIN_COLOR_B"; PLYValue[PLYValue["MAX_COLOR_R"] = 37] = "MAX_COLOR_R"; PLYValue[PLYValue["MAX_COLOR_G"] = 38] = "MAX_COLOR_G"; PLYValue[PLYValue["MAX_COLOR_B"] = 39] = "MAX_COLOR_B"; PLYValue[PLYValue["SH_0"] = 40] = "SH_0"; PLYValue[PLYValue["SH_1"] = 41] = "SH_1"; PLYValue[PLYValue["SH_2"] = 42] = "SH_2"; PLYValue[PLYValue["SH_3"] = 43] = "SH_3"; PLYValue[PLYValue["SH_4"] = 44] = "SH_4"; PLYValue[PLYValue["SH_5"] = 45] = "SH_5"; PLYValue[PLYValue["SH_6"] = 46] = "SH_6"; PLYValue[PLYValue["SH_7"] = 47] = "SH_7"; PLYValue[PLYValue["SH_8"] = 48] = "SH_8"; PLYValue[PLYValue["SH_9"] = 49] = "SH_9"; PLYValue[PLYValue["SH_10"] = 50] = "SH_10"; PLYValue[PLYValue["SH_11"] = 51] = "SH_11"; PLYValue[PLYValue["SH_12"] = 52] = "SH_12"; PLYValue[PLYValue["SH_13"] = 53] = "SH_13"; PLYValue[PLYValue["SH_14"] = 54] = "SH_14"; PLYValue[PLYValue["SH_15"] = 55] = "SH_15"; PLYValue[PLYValue["SH_16"] = 56] = "SH_16"; PLYValue[PLYValue["SH_17"] = 57] = "SH_17"; PLYValue[PLYValue["SH_18"] = 58] = "SH_18"; PLYValue[PLYValue["SH_19"] = 59] = "SH_19"; PLYValue[PLYValue["SH_20"] = 60] = "SH_20"; PLYValue[PLYValue["SH_21"] = 61] = "SH_21"; PLYValue[PLYValue["SH_22"] = 62] = "SH_22"; PLYValue[PLYValue["SH_23"] = 63] = "SH_23"; PLYValue[PLYValue["SH_24"] = 64] = "SH_24"; PLYValue[PLYValue["SH_25"] = 65] = "SH_25"; PLYValue[PLYValue["SH_26"] = 66] = "SH_26"; PLYValue[PLYValue["SH_27"] = 67] = "SH_27"; PLYValue[PLYValue["SH_28"] = 68] = "SH_28"; PLYValue[PLYValue["SH_29"] = 69] = "SH_29"; PLYValue[PLYValue["SH_30"] = 70] = "SH_30"; PLYValue[PLYValue["SH_31"] = 71] = "SH_31"; PLYValue[PLYValue["SH_32"] = 72] = "SH_32"; PLYValue[PLYValue["SH_33"] = 73] = "SH_33"; PLYValue[PLYValue["SH_34"] = 74] = "SH_34"; PLYValue[PLYValue["SH_35"] = 75] = "SH_35"; PLYValue[PLYValue["SH_36"] = 76] = "SH_36"; PLYValue[PLYValue["SH_37"] = 77] = "SH_37"; PLYValue[PLYValue["SH_38"] = 78] = "SH_38"; PLYValue[PLYValue["SH_39"] = 79] = "SH_39"; PLYValue[PLYValue["SH_40"] = 80] = "SH_40"; PLYValue[PLYValue["SH_41"] = 81] = "SH_41"; PLYValue[PLYValue["SH_42"] = 82] = "SH_42"; PLYValue[PLYValue["SH_43"] = 83] = "SH_43"; PLYValue[PLYValue["SH_44"] = 84] = "SH_44"; PLYValue[PLYValue["UNDEFINED"] = 85] = "UNDEFINED"; })(PLYValue || (PLYValue = {})); /** * Class used to render a gaussian splatting mesh */ class GaussianSplattingMesh extends Mesh { /** * View direction factor used to compute the SH view direction in the shader. */ get viewDirectionFactor() { return this._viewDirectionFactor; } /** * SH degree. 0 = no sh (default). 1 = 3 parameters. 2 = 8 parameters. 3 = 15 parameters. */ get shDegree() { return this._shDegree; } /** * returns the splats data array buffer that contains in order : postions (3 floats), size (3 floats), color (4 bytes), orientation quaternion (4 bytes) */ get splatsData() { return this._splatsData; } /** * Gets the covariancesA texture */ get covariancesATexture() { return this._covariancesATexture; } /** * Gets the covariancesB texture */ get covariancesBTexture() { return this._covariancesBTexture; } /** * Gets the centers texture */ get centersTexture() { return this._centersTexture; } /** * Gets the colors texture */ get colorsTexture() { return this._colorsTexture; } /** * Gets the SH textures */ get shTextures() { return this._shTextures; } /** * set rendering material */ set material(value) { this._material = value; this._material.backFaceCulling = true; this._material.cullBackFaces = false; value.resetDrawCache(); } /** * get rendering material */ get material() { return this._material; } /** * Creates a new gaussian splatting mesh * @param name defines the name of the mesh * @param url defines the url to load from (optional) * @param scene defines the hosting scene (optional) * @param keepInRam keep datas in ram for editing purpose */ constructor(name, url = null, scene = null, keepInRam = false) { super(name, scene); this._vertexCount = 0; this._worker = null; this._frameIdLastUpdate = -1; this._modelViewMatrix = Matrix.Identity(); this._canPostToWorker = true; this._readyToDisplay = false; this._covariancesATexture = null; this._covariancesBTexture = null; this._centersTexture = null; this._colorsTexture = null; this._splatPositions = null; this._splatIndex = null; this._shTextures = null; this._splatsData = null; this._sh = null; this._keepInRam = false; this._delayedTextureUpdate = null; this._oldDirection = new Vector3(); this._useRGBACovariants = false; this._material = null; this._tmpCovariances = [0, 0, 0, 0, 0, 0]; this._sortIsDirty = false; this._shDegree = 0; this._viewDirectionFactor = new Vector3(1, 1, -1); const vertexData = new VertexData(); // Use an intanced quad or triangle. Triangle might be a bit faster because of less shader invocation but I didn't see any difference. // Keeping both and use triangle for now. // for quad, use following lines //vertexData.positions = [-2, -2, 0, 2, -2, 0, 2, 2, 0, -2, 2, 0]; //vertexData.indices = [0, 1, 2, 0, 2, 3]; vertexData.positions = [-3, -2, 0, 3, -2, 0, 0, 4, 0]; vertexData.indices = [0, 1, 2]; vertexData.applyToMesh(this); this.subMeshes = []; // for quad, use following line //new SubMesh(0, 0, 4, 0, 6, this); new SubMesh(0, 0, 3, 0, 3, this); this.setEnabled(false); // webGL2 and webGPU support for RG texture with float16 is fine. not webGL1 this._useRGBACovariants = !this.getEngine().isWebGPU && this.getEngine().version === 1.0; this._keepInRam = keepInRam; if (url) { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.loadFileAsync(url); } this._material = new GaussianSplattingMaterial(this.name + "_material", this._scene); } /** * Returns the class name * @returns "GaussianSplattingMesh" */ getClassName() { return "GaussianSplattingMesh"; } /** * Returns the total number of vertices (splats) within the mesh * @returns the total number of vertices */ getTotalVertices() { return this._vertexCount; } /** * Is this node ready to be used/rendered * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default) * @returns true when ready */ isReady(completeCheck = false) { if (!super.isReady(completeCheck, true)) { return false; } if (!this._readyToDisplay) { // mesh is ready when worker has done at least 1 sorting this._postToWorker(true); return false; } return true; } /** @internal */ _postToWorker(forced = false) { const frameId = this.getScene().getFrameId(); if ((forced || frameId !== this._frameIdLastUpdate) && this._worker && this._scene.activeCamera && this._canPostToWorker) { const cameraMatrix = this._scene.activeCamera.getViewMatrix(); this.getWorldMatrix().multiplyToRef(cameraMatrix, this._modelViewMatrix); cameraMatrix.invertToRef(TmpVectors.Matrix[0]); this.getWorldMatrix().multiplyToRef(TmpVectors.Matrix[0], TmpVectors.Matrix[1]); Vector3.TransformNormalToRef(Vector3.Forward(this._scene.useRightHandedSystem), TmpVectors.Matrix[1], TmpVectors.Vector3[2]); TmpVectors.Vector3[2].normalize(); const dot = Vector3.Dot(TmpVectors.Vector3[2], this._oldDirection); if (forced || Math.abs(dot - 1) >= 0.01) { this._oldDirection.copyFrom(TmpVectors.Vector3[2]); this._frameIdLastUpdate = frameId; this._canPostToWorker = false; this._worker.postMessage({ view: this._modelViewMatrix.m, depthMix: this._depthMix, useRightHandedSystem: this._scene.useRightHandedSystem }, [ this._depthMix.buffer, ]); } } } /** * Triggers the draw call for the mesh. Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager * @param subMesh defines the subMesh to render * @param enableAlphaMode defines if alpha mode can be changed * @param effectiveMeshReplacement defines an optional mesh used to provide info for the rendering * @returns the current mesh */ render(subMesh, enableAlphaMode, effectiveMeshReplacement) { this._postToWorker(); return super.render(subMesh, enableAlphaMode, effectiveMeshReplacement); } static _TypeNameToEnum(name) { switch (name) { case "float": return 0 /* PLYType.FLOAT */; case "int": return 1 /* PLYType.INT */; case "uint": return 2 /* PLYType.UINT */; case "double": return 3 /* PLYType.DOUBLE */; case "uchar": return 4 /* PLYType.UCHAR */; } return 5 /* PLYType.UNDEFINED */; } static _ValueNameToEnum(name) { switch (name) { case "min_x": return 0 /* PLYValue.MIN_X */; case "min_y": return 1 /* PLYValue.MIN_Y */; case "min_z": return 2 /* PLYValue.MIN_Z */; case "max_x": return 3 /* PLYValue.MAX_X */; case "max_y": return 4 /* PLYValue.MAX_Y */; case "max_z": return 5 /* PLYValue.MAX_Z */; case "min_scale_x": return 6 /* PLYValue.MIN_SCALE_X */; case "min_scale_y": return 7 /* PLYValue.MIN_SCALE_Y */; case "min_scale_z": return 8 /* PLYValue.MIN_SCALE_Z */; case "max_scale_x": return 9 /* PLYValue.MAX_SCALE_X */; case "max_scale_y": return 10 /* PLYValue.MAX_SCALE_Y */; case "max_scale_z": return 11 /* PLYValue.MAX_SCALE_Z */; case "packed_position": return 12 /* PLYValue.PACKED_POSITION */; case "packed_rotation": return 13 /* PLYValue.PACKED_ROTATION */; case "packed_scale": return 14 /* PLYValue.PACKED_SCALE */; case "packed_color": return 15 /* PLYValue.PACKED_COLOR */; case "x": return 16 /* PLYValue.X */; case "y": return 17 /* PLYValue.Y */; case "z": return 18 /* PLYValue.Z */; case "scale_0": return 19 /* PLYValue.SCALE_0 */; case "scale_1": return 20 /* PLYValue.SCALE_1 */; case "scale_2": return 21 /* PLYValue.SCALE_2 */; case "diffuse_red": case "red": return 22 /* PLYValue.DIFFUSE_RED */; case "diffuse_green": case "green": return 23 /* PLYValue.DIFFUSE_GREEN */; case "diffuse_blue": case "blue": return 24 /* PLYValue.DIFFUSE_BLUE */; case "f_dc_0": return 26 /* PLYValue.F_DC_0 */; case "f_dc_1": return 27 /* PLYValue.F_DC_1 */; case "f_dc_2": return 28 /* PLYValue.F_DC_2 */; case "f_dc_3": return 29 /* PLYValue.F_DC_3 */; case "opacity": return 25 /* PLYValue.OPACITY */; case "rot_0": return 30 /* PLYValue.ROT_0 */; case "rot_1": return 31 /* PLYValue.ROT_1 */; case "rot_2": return 32 /* PLYValue.ROT_2 */; case "rot_3": return 33 /* PLYValue.ROT_3 */; case "min_r": return 34 /* PLYValue.MIN_COLOR_R */; case "min_g": return 35 /* PLYValue.MIN_COLOR_G */;