From f328d16babedd213ccff8197df0ced6cdd00707f Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 17 Mar 2024 15:20:30 -0400 Subject: [PATCH] sending grass half-baked because I want water --- assets/Data/foliage.json | 2 +- assets/Models/grass2.fbx | Bin 0 -> 11692 bytes assets/Models/modelPretransforms.json | 16 + assets/Shaders/foliage/foliage.fs | 36 ++- assets/Shaders/foliage/foliage.vs | 296 +++++++++++++++++- buildNumber.properties | 4 +- .../architecture/archimprovementtargets.md | 7 + docs/src/architecture/architectureindex.md | 1 + .../creatures/mechanicsideas.md | 1 + .../fluid/fluidbifrication.md | 25 ++ .../fluid/fluidcellularautomata.md | 11 + docs/src/highlevel-design/fluid/fluidchunk.md | 17 + docs/src/highlevel-design/fluid/fluidindex.md | 6 + .../highlevel-design/highleveldesignindex.md | 4 +- .../locations/largelocationideas.md | 3 +- .../highlevel-design/locomotion/locomotion.md | 22 ++ .../narrativemanager/narrativemanager.md | 6 + .../narrativemanager/whatmakesaquestgood.md | 33 ++ .../highlevel-design/puzzles/puzzleindex.md | 3 +- docs/src/progress/bigthings.md | 19 ++ docs/src/progress/renderertodo.md | 50 +-- .../{indexdrawcell.md => indexrendering.md} | 4 +- .../rendering/instancing/instancingarch.md | 3 +- .../foliagemanager/ClientFoliageManager.java | 262 ++++++++++++---- .../client/foliagemanager/FoliageCell.java | 19 +- .../entity/EntityDataStrings.java | 1 + .../renderer/RenderPipelineState.java | 11 + .../electrosphere/renderer/actor/Actor.java | 2 - .../actor/instance/TextureInstancedActor.java | 121 +++++++ .../renderer/meshgen/MeshLoader.java | 9 +- .../electrosphere/renderer/model/Mesh.java | 13 +- .../pipelines/MainContentPipeline.java | 1 + .../renderer/pipelines/ShadowMapPipeline.java | 2 +- .../pipelines/VolumeBufferPipeline.java | 2 +- .../renderer/texture/Texture.java | 25 ++ 35 files changed, 912 insertions(+), 125 deletions(-) create mode 100644 assets/Models/grass2.fbx create mode 100644 docs/src/architecture/archimprovementtargets.md create mode 100644 docs/src/highlevel-design/fluid/fluidbifrication.md create mode 100644 docs/src/highlevel-design/fluid/fluidcellularautomata.md create mode 100644 docs/src/highlevel-design/fluid/fluidchunk.md create mode 100644 docs/src/highlevel-design/fluid/fluidindex.md create mode 100644 docs/src/highlevel-design/locomotion/locomotion.md create mode 100644 docs/src/highlevel-design/narrativemanager/whatmakesaquestgood.md create mode 100644 docs/src/progress/bigthings.md rename docs/src/rendering/{indexdrawcell.md => indexrendering.md} (51%) create mode 100644 src/main/java/electrosphere/renderer/actor/instance/TextureInstancedActor.java diff --git a/assets/Data/foliage.json b/assets/Data/foliage.json index ee9ed1cc..771b2435 100644 --- a/assets/Data/foliage.json +++ b/assets/Data/foliage.json @@ -29,7 +29,7 @@ "growthModel": { "growthRate" : 0.001 }, - "modelPath" : "Models/grass1.fbx" + "modelPath" : "Models/grass2.fbx" }, { "name" : "oak", diff --git a/assets/Models/grass2.fbx b/assets/Models/grass2.fbx new file mode 100644 index 0000000000000000000000000000000000000000..fdf150f5736e9f28aee771c751a3999ff192dd6f GIT binary patch literal 11692 zcmc&)dvH|M8NZQ0NI(#@78NlTG$0y+6cMXJ9t5}KWj2X`IOS&dBwX0NcYF6PZhZ1^ zI-}#L)2UOnW9exBu{tfSwbqV~h*p_StB&KN)=srJj&am#t5tl`-|yUWch4q!cbD)- z&*bLbd%pAi&i6XsdF)+hj>JvZtZ#2wU*BTcraMw!Z=7t5&NYmuYm8i(4f-X*j0<;7 z))RKxa_mhYR^ghA>$n_wK)57E-koF^S*=AKS_CwL-=WcFtg&P6NXi3W-$+VCKr(g` zT9sL?t}rFlN{XmwCR5LW67*e@gGHBbX@u$x!gM2}(Bez2c5;ffHJAg z9Er?8WrJ2(eTkECBePJuN~@jHY1tW1M3$lcGQB=3VvZe`D4te7t*th7)3Ej!lB ze%10ERMtpE9XGlVFD(gS!`}4^)1En)5Yj`D?nU+I)GupX-nbO^Z#OPm(YX8(D0mFo zH{hy`#)K^*_K*|(242@DoIx|eSEDLVnF>mOTDWbdXPyd92w)tGtD44->eTZTl`hVi zIgXbpnif+#g=Z2g9@hG5jWD^-@)9Bn4C6;ybxkxgxK+eFNq9gbOli(|TVN3~JgwDF zT_;AabliAaQr^%hy<)q?7?hOHG)hgYlS~TR?}UK4@_-iV(|cSeC0x%E=@m^wP?XxQ z#8uT3orYVR6Uz{#-%Uixwl3 z20QB-p{3#D%*Im?kL`q)EO#p8Zcu?d2e)F#T_#3taolfexMSk{Z3VvC2R|hQTn+vZ zeCjPnOADU((&?jdaO6Zq?20~QmhY|Y?2|SOD)KgrQqZQQOYpaJ<%*{Irlt$zUt?2K z)6EK6`Igxt2ia4kT!8oPi7Wti`RvOnXstyNHK4_v9%BdeKTbmZS7; zRzdgp(wr0qT|&PS?tMm~H~Z+A5yAwQ^GP}X&PROFzg<4ui3MHGI|2-f1g6(MT=>@YYUB%Eo zr)eqv>X)hjN0>Lo71M-gsDsl1uF4F5PKpZ8L()v&p|I(@+zc~{9QTsOQ;?BI!NJyx zaAA-jjEw$NbJj}B1Z*L@Gu1?FPYerx+K5o&`4~-PqY_}N!%eYS!**%bD-`S5J}ZZ1 zyNg9c=}s~47(Y>*^gKG{B%9|f+wV)``)zeF<(2LieHh0>Ju0j9o98UsZ?EEB3Is^a z$tsBVa+W@fa=T(*?z4017{gvxg4Yzp`98!<-1=?Hi^j}^Xg6aX<|+C;?qo1X=>(h93_K@UcFPLLxdbt2D1IkN~mlg!U*5zap?ie;J4LJ7zcO_?^9+O8*cStqNAARlm-%L(3% zT?*h_Jflw{-Huqw5| zk}EwEiZUj%;Cej5>pIVTAoraLLFru*LPFvs&?3zpHakSl`5tPAc z=x0?mRjuV2rVmtslDkvvl^~Fm1L`UO5SSy^Q@Z6DFq?15G-Sdi-@q3)w z0*v&DWGcZ3^zkYnvZ#e9~}OrN!4xtp$!ZZYG|mFY0P_Z8pSJ|E0v^op23 z`YXqYmX(x;5!;LqW+y$0s3}uvqC>?BE3^>$h2kq2LL7s4qhU}ALg-ll6IA6=F6S=I zApTsNXDV3JxG6`*V>Kvak~skz-~oDg9WMBTgQ(Z>OpeU%p&_h0C+g_y74Ua`VEQTm z?yEDeuN-bUmT8Y1G$OOGhrqY`&3VCkPm9~Ry+`drga!zVDYpQ?+6 zE!r7RDv>nbXuL-;tmk4iFvjUR@We;=0D!5jHqdln%U&O*>uu#YH-vHisT}9VFwSa> zmJ-sM*-&XOF2@-vZMGa|sI+?(r#0i7PZFb^0>zpIoog*F@b6K4+>SwMLka1J6hO}l&Xs% z)tFpgIuS*1P&Y=)K$fG(LVHS}PCNy@3{?7ak*_54xF&1ph&~qMBAhLBIJcE#3mwZ> zmtZTAc79WmEmX*NO0tCtc^VFWX_AG$B^lchdy8-_Pp-E^78I8$E-g)|d9-wlJpew& z*jm3guA~&-QG{3(jyFR&Jo_Dt|^ic3ww%uxL)e;Hbmul8HM-e6}^ zX2;Pj&XvWsPgS6GtLEw$7ITi#C5ExD-7avzjL!-@9LwPQfJ`do;8d7!Mb30?FGRs+ zB{dt#Z$#HCT`5sUUb3&d}o=5oBA3D_a%YykqwbAAfYk z7ds!F)Vuh{uRXcuq&b@pPM>k^*7}h-#*MpQG@~uJ!Fu$VaH~lA}fcR#qeMWEXB4o&sP|2 ztUm@9HTeSX9NK*T49|lKcPZ=E#D_)N;;#uT@VzXAB0J&8HU$@l7TYm|tcJXyW_Oq) z_=GGW_yJ9q<0kQS5}Fu$R5#?${c0fa@{mTTmcMe#+Z})U1KaGK79D~*s?tv5B7b3mGRh`Iai@YzRbcFmV8ukhHi%b0cnDJG&2>L;4wv@EqxOZ zqJIE-f2>GHXotm^fG7&iUQ5`4>s1*%eU5LAhZWnkARN;_5a;~GlJalH8`u7Dm3*9A zBk$^c$0Ji-=KH==;veYr;60!lwJ+=^{f$X~d5ibeH@3d3zwf9!^g7@7-Fx_hRn^Lx zjQ1K?kaJ2wkW)XFATs$LL?{D12y%M6pv*(!2>diz`boN{S#w{`J#lc}4?<9;>wev( zIB-m(KCPp@^%j0e{ZvD)<6b;2wTT3vx%(_g7Lwkd^Ks!TO8yRV)4sSgwEWdqf$+!0`5p?g>?k;DK%2<) z`NHIe24ly-zP126_i!P)BWgRYiA zZF0-58xAEK$4Cu-bVC|#ty->XnLuk=Gx&5B zA2-(FMLpw(8N7s){mhlBOlx&)o8KkiXW?5^?BMLmsA`R}$L{W2_;ds;4;5_|%-Xs3 zL34TkT4u+zsjMF WH*fvNe_y@sy?3U+nYne*wEqD(=ZpdX literal 0 HcmV?d00001 diff --git a/assets/Models/modelPretransforms.json b/assets/Models/modelPretransforms.json index 541f22ff..13763b1c 100644 --- a/assets/Models/modelPretransforms.json +++ b/assets/Models/modelPretransforms.json @@ -64,6 +64,22 @@ }, "meshes" : [ ] + }, + { + "path" : "Models/grass2.fbx", + "globalTransform": { + "rotation" : [0.0, 0.0, 0.0, 1.0], + "offset" : [0.0, 0.0, 0.0], + "scale" : [1.0, 1.0, 1.0] + }, + "meshes" : [ + { + "meshName" : "Plane", + "rotation" : [-0.7071068, 0.0, 0.0, 0.7071068], + "offset" : [0.0, 0.0, 0.0], + "scale" : [1.0, 1.0, 1.0] + } + ] } ] } \ No newline at end of file diff --git a/assets/Shaders/foliage/foliage.fs b/assets/Shaders/foliage/foliage.fs index ff8f9853..a9e0c03a 100644 --- a/assets/Shaders/foliage/foliage.fs +++ b/assets/Shaders/foliage/foliage.fs @@ -42,6 +42,8 @@ in vec3 FragPos; in vec3 Normal; in vec2 TexCoord; in vec4 FragPosLightSpace; +in vec3 normalRot1; +in vec3 normalRot2; uniform vec3 viewPos; @@ -65,29 +67,46 @@ uniform sampler2D shadowMap; // vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir); float calcLightIntensityTotal(vec3 normal); float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal); +float easeIn(float interpolator); +float easeOut(float interpolator); void main(){ - vec3 norm = normalize(Normal); + + //basic vars + float heightPercent = TexCoord.y; + + //calculate color + vec3 baseColor = vec3(0.05,0.2,0.01); + vec3 tipColor = vec3(0.25,0.45,0); + vec3 textureColor = mix(baseColor,tipColor,easeIn(heightPercent)); + + //mix normals + float normalMix = TexCoord.x; + float normalMultiplier = -(1.0 + -2.0 * int(gl_FrontFacing)); + vec3 norm = normalize(mix(normalRot1,normalRot2,normalMix) * normalMultiplier); + // vec3 norm = normalize(Normal * normalMultiplier); vec3 viewDir = normalize(viewPos - FragPos); //grab light intensity float lightIntensity = calcLightIntensityTotal(norm); //get color of base texture - vec3 textureColor = vec3(0.17647,0.4,0.09411);//texture(material.diffuse, TexCoord).rgb; + // vec3 textureColor = vec3((norm.x + 1) / 2.0, norm.y, 1.0 - (norm.x + 1) / 2.0); + // vec3 textureColor = vec3(TexCoord,1.0); + // vec3 textureColor = vec3(0.17647,0.4,0.09411);//texture(material.diffuse, TexCoord).rgb; //shadow float shadow = ShadowCalculation(FragPosLightSpace, normalize(-dLDirection), norm); //calculate final color - vec3 finalColor = textureColor * lightIntensity * max(shadow,0.4); + vec3 finalColor = textureColor * lightIntensity;// * max(shadow,0.4); // vec3 lightAmount = CalcDirLight(norm, viewDir); // for(int i = 0; i < NR_POINT_LIGHTS; i++){ // lightAmount += CalcPointLight(i, norm, FragPos, viewDir); // } //this final calculation is for transparency - FragColor = vec4(finalColor, texture(material.diffuse, TexCoord).a);//texture(ourTexture, TexCoord);//vec4(result, 1.0); + FragColor = vec4(finalColor, 1.0);//texture(ourTexture, TexCoord);//vec4(result, 1.0); } // calculates the color when using a directional light. @@ -235,4 +254,13 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){ // shadow = currentDepth; return shadow; +} + + +float easeIn(float interpolator){ + return interpolator * interpolator; +} + +float easeOut(float interpolator){ + return 1 - easeIn(1 - interpolator); } \ No newline at end of file diff --git a/assets/Shaders/foliage/foliage.vs b/assets/Shaders/foliage/foliage.vs index a3bd10da..fc19ba37 100644 --- a/assets/Shaders/foliage/foliage.vs +++ b/assets/Shaders/foliage/foliage.vs @@ -1,23 +1,38 @@ //Vertex Shader -#version 330 core +#version 430 core +//defines +#define PI 3.1415 + +#define grassWidth 0.01 //TODO: convert to uniform + + //input buffers layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 4) in vec2 aTex; -layout (location = 5) in vec4 modelA; -layout (location = 6) in vec4 modelB; -layout (location = 7) in vec4 modelC; -layout (location = 8) in vec4 modelD; +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +uniform Material material; + +uniform sampler2D dataMap; + //coordinate space transformation matrices uniform mat4 transform; +uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat4 lightSpaceMatrix; +uniform vec3 viewPos; +uniform float time; @@ -26,21 +41,101 @@ out vec3 Normal; out vec3 FragPos; out vec2 TexCoord; out vec4 FragPosLightSpace; +out vec3 normalRot1; +out vec3 normalRot2; +//defines +mat4 rotation3dX(float angle); +mat4 rotation3dY(float angle); +mat4 rotation3dZ(float angle); +vec3 rotateY(vec3 vector, float angle); +float easeIn(float interpolator); +float easeOut(float interpolator); +float easeInOut(float interpolator); +float map(float value, float min1, float max1, float min2, float max2); +mat4 rotationMatrix(vec3 axis, float angle); + +//lib defines +vec4 openSimplex2_Conventional(vec3 X); +vec4 openSimplex2_ImproveXY(vec3 X); void main() { + + //0 = left, 1 = right + float xDirection = mod(float(gl_VertexID), 2.0); + + ivec2 texSize = textureSize(material.diffuse,0); + + //grab data out of texture + float xOffset = texelFetch(material.diffuse,ivec2(0,gl_InstanceID),0).r; + float yOffset = texelFetch(material.diffuse,ivec2(1,gl_InstanceID),0).r; + float zOffset = texelFetch(material.diffuse,ivec2(2,gl_InstanceID),0).r; + float rotVar = texelFetch(material.diffuse,ivec2(3,gl_InstanceID),0).r; + float rotVar2 = texelFetch(material.diffuse,ivec2(4,gl_InstanceID),0).r; + + // + //curve float noise + vec4 worldPos = model * vec4(aPos.x + xOffset,aPos.y + yOffset,aPos.z + zOffset,1.0); + float curveFloatNoiseSample = clamp(map(openSimplex2_ImproveXY(vec3(worldPos.x,worldPos.z,time)).x,-1.0,1.0,0,1),0,1); + + // + //calculate rotations + float curveAmount = rotVar2 * aPos.y * 5 + curveFloatNoiseSample.x * 0.01; + mat4 localRot = rotation3dY(rotVar); + mat4 localRot2 = rotation3dZ(curveAmount); + + // + //rotate with wind + float windDir = clamp(map(openSimplex2_ImproveXY(vec3(worldPos.x,worldPos.z,0) * 0.05 + 0.05 * time * vec3(1,1,0)).x,-1.0,1.0,0,1),0,1); + windDir = map(windDir,0.0,1.0,0.0,PI * 2.0); + //strength + float windStrength = clamp(map(openSimplex2_ImproveXY(vec3(worldPos.x,worldPos.z,0) * 0.25 + 0.2 * time * vec3(1,1,0)).x,-3.0,3.0,0,1),0,1); + //try to shape with easeIn + float windLeanAngle = map(windStrength, 0.0, 1.0, 0.1, 0.9); + windLeanAngle = easeIn(windLeanAngle) * 1.25; + mat4 windRot = rotationMatrix(vec3(cos(windDir),0,sin(windDir)),windLeanAngle); + + + // + //position transform + mat4 localTransform = mat4( + 1.0, 0.0, 0.0, 0.0, //column 1 + 0.0, 1.0, 0.0, 0.0, //column 2 + 0.0, 0.0, 1.0, 0.0, //column 3 + xOffset, yOffset, zOffset, 1.0 //column 4 + ); + //normalize posiiton and normal - vec4 FinalVertex = vec4(aPos, 1.0); - vec4 FinalNormal = vec4(aNormal, 1.0); + vec4 FinalVertex = model * localTransform * windRot * localRot * localRot2 * vec4(aPos, 1.0); + vec4 FinalNormal = windRot * localRot * localRot2 * vec4(aNormal, 1.0); + // vec4 FinalNormal = transpose(inverse(localRot2 * localRot * model * localTransform)) * vec4(aNormal, 1.0); - mat4 model = mat4(modelA,modelB,modelC,modelD); + //normal offset + normalRot1 = rotateY(FinalNormal.rgb,PI * 0.3); + normalRot2 = rotateY(FinalNormal.rgb,PI * -0.3); + // + //shift in viewspace to make it feel slightly fuller + // + //dot view and normal + vec3 viewDir = normalize(viewPos - FinalVertex.xyz); + float viewDotNormal = clamp(dot(FinalNormal.xz,viewDir.xz),0,1); + //calculate thinkening factor to shift verts slightly based on view angle + float viewSpaceThickenFactor = easeOut(1.0 - viewDotNormal); + //as blade starts to become parallel with camera, want to allow it to shrink into nothing + viewSpaceThickenFactor *= smoothstep(0.0, 0.2, viewDotNormal); + //finally, apply adjustment to actual vert output + FinalVertex.x += viewSpaceThickenFactor * (xDirection - 0.5) * grassWidth; + + + // //push frag, normal, and texture positions to fragment shader - FragPos = vec3(model * FinalVertex); - Normal = mat3(transpose(inverse(model))) * aNormal; + // + FragPos = vec3(FinalVertex); + Normal = vec3(FinalNormal); TexCoord = aTex; @@ -49,5 +144,184 @@ void main() { //set final position with opengl space - gl_Position = projection * view * model * FinalVertex; + gl_Position = projection * view * FinalVertex; } + +mat4 rotation3dX(float angle) { + float s = sin(angle); + float c = cos(angle); + + mat4 rVal = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, c, s, 0.0, + 0.0, -s, c, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + return rVal; +} + +vec3 rotateY(vec3 vector, float angle){ + mat4 mat = rotation3dY(angle); + return (mat * vec4(vector,1.0)).xyz; +} + +mat4 rotation3dY(float angle) { + float s = sin(angle); + float c = cos(angle); + + mat4 rVal = mat4( + c, 0.0, -s, 0.0, + 0.0, 1.0, 0.0, 0.0, + s, 0.0, c, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + return rVal; +} + +mat4 rotation3dZ(float angle) { + float s = sin(angle); + float c = cos(angle); + + mat4 rVal = mat4( + c, s, 0.0, 0.0, + -s, c, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + return rVal; +} + + +float easeIn(float interpolator){ + return interpolator * interpolator; +} + +float easeOut(float interpolator){ + return 1 - easeIn(1 - interpolator); +} + +float easeInOut(float interpolator){ + float easeInValue = easeIn(interpolator); + float easeOutValue = easeOut(interpolator); + return mix(easeInValue, easeOutValue, interpolator); +} + +float map(float value, float min1, float max1, float min2, float max2) { + return min2 + (value - min1) * (max2 - min2) / (max1 - min1); +} + + +mat4 rotationMatrix(vec3 axis, float angle){ + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + + +//////////////// K.jpg's Re-oriented 4-Point BCC Noise (OpenSimplex2) //////////////// +////////////////////// Output: vec4(dF/dx, dF/dy, dF/dz, value) ////////////////////// + +// Inspired by Stefan Gustavson's noise +vec4 permute(vec4 t) { + return t * (t * 34.0 + 133.0); +} + +// Gradient set is a normalized expanded rhombic dodecahedron +vec3 grad(float hash) { + + // Random vertex of a cube, +/- 1 each + vec3 cube = mod(floor(hash / vec3(1.0, 2.0, 4.0)), 2.0) * 2.0 - 1.0; + + // Random edge of the three edges connected to that vertex + // Also a cuboctahedral vertex + // And corresponds to the face of its dual, the rhombic dodecahedron + vec3 cuboct = cube; + cuboct[int(hash / 16.0)] = 0.0; + + // In a funky way, pick one of the four points on the rhombic face + float type = mod(floor(hash / 8.0), 2.0); + vec3 rhomb = (1.0 - type) * cube + type * (cuboct + cross(cube, cuboct)); + + // Expand it so that the new edges are the same length + // as the existing ones + vec3 grad = cuboct * 1.22474487139 + rhomb; + + // To make all gradients the same length, we only need to shorten the + // second type of vector. We also put in the whole noise scale constant. + // The compiler should reduce it into the existing floats. I think. + grad *= (1.0 - 0.042942436724648037 * type) * 32.80201376986577; + + return grad; +} + +// BCC lattice split up into 2 cube lattices +vec4 openSimplex2Base(vec3 X) { + + // First half-lattice, closest edge + vec3 v1 = round(X); + vec3 d1 = X - v1; + vec3 score1 = abs(d1); + vec3 dir1 = step(max(score1.yzx, score1.zxy), score1); + vec3 v2 = v1 + dir1 * sign(d1); + vec3 d2 = X - v2; + + // Second half-lattice, closest edge + vec3 X2 = X + 144.5; + vec3 v3 = round(X2); + vec3 d3 = X2 - v3; + vec3 score2 = abs(d3); + vec3 dir2 = step(max(score2.yzx, score2.zxy), score2); + vec3 v4 = v3 + dir2 * sign(d3); + vec3 d4 = X2 - v4; + + // Gradient hashes for the four points, two from each half-lattice + vec4 hashes = permute(mod(vec4(v1.x, v2.x, v3.x, v4.x), 289.0)); + hashes = permute(mod(hashes + vec4(v1.y, v2.y, v3.y, v4.y), 289.0)); + hashes = mod(permute(mod(hashes + vec4(v1.z, v2.z, v3.z, v4.z), 289.0)), 48.0); + + // Gradient extrapolations & kernel function + vec4 a = max(0.5 - vec4(dot(d1, d1), dot(d2, d2), dot(d3, d3), dot(d4, d4)), 0.0); + vec4 aa = a * a; vec4 aaaa = aa * aa; + vec3 g1 = grad(hashes.x); vec3 g2 = grad(hashes.y); + vec3 g3 = grad(hashes.z); vec3 g4 = grad(hashes.w); + vec4 extrapolations = vec4(dot(d1, g1), dot(d2, g2), dot(d3, g3), dot(d4, g4)); + + // Derivatives of the noise + vec3 derivative = -8.0 * mat4x3(d1, d2, d3, d4) * (aa * a * extrapolations) + + mat4x3(g1, g2, g3, g4) * aaaa; + + // Return it all as a vec4 + return vec4(derivative, dot(aaaa, extrapolations)); +} + +// Use this if you don't want Z to look different from X and Y +vec4 openSimplex2_Conventional(vec3 X) { + + // Rotate around the main diagonal. Not a skew transform. + vec4 result = openSimplex2Base(dot(X, vec3(2.0/3.0)) - X); + return vec4(dot(result.xyz, vec3(2.0/3.0)) - result.xyz, result.w); +} + +// Use this if you want to show X and Y in a plane, then use Z for time, vertical, etc. +vec4 openSimplex2_ImproveXY(vec3 X) { + + // Rotate so Z points down the main diagonal. Not a skew transform. + mat3 orthonormalMap = mat3( + 0.788675134594813, -0.211324865405187, -0.577350269189626, + -0.211324865405187, 0.788675134594813, -0.577350269189626, + 0.577350269189626, 0.577350269189626, 0.577350269189626); + + vec4 result = openSimplex2Base(orthonormalMap * X); + return vec4(result.xyz * orthonormalMap, result.w); +} + +//////////////////////////////// End noise code //////////////////////////////// \ No newline at end of file diff --git a/buildNumber.properties b/buildNumber.properties index 79ad097a..65462928 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Tue Mar 12 22:56:01 EDT 2024 -buildNumber=47 +#Sun Mar 17 09:54:23 EDT 2024 +buildNumber=75 diff --git a/docs/src/architecture/archimprovementtargets.md b/docs/src/architecture/archimprovementtargets.md new file mode 100644 index 00000000..5a821cc9 --- /dev/null +++ b/docs/src/architecture/archimprovementtargets.md @@ -0,0 +1,7 @@ +@page archimprovementtargets Architecture Improvement Targets + +Parts of the engine that can be better organized: + +#### Collision Body description in data +Creatures, plants, objects, etc can define collision bodies and we should unify that code so it can be reused between all of them. + diff --git a/docs/src/architecture/architectureindex.md b/docs/src/architecture/architectureindex.md index 0870a18f..16735ac5 100644 --- a/docs/src/architecture/architectureindex.md +++ b/docs/src/architecture/architectureindex.md @@ -7,6 +7,7 @@ - @subpage uiarch - @subpage audioengine - @subpage timekeeper +- @subpage archimprovementtargets # What is this section diff --git a/docs/src/highlevel-design/creatures/mechanicsideas.md b/docs/src/highlevel-design/creatures/mechanicsideas.md index f875e50b..9d8efded 100644 --- a/docs/src/highlevel-design/creatures/mechanicsideas.md +++ b/docs/src/highlevel-design/creatures/mechanicsideas.md @@ -1,5 +1,6 @@ @page creaturemechanicsideas Mechanics Ideas If/when fluidsim is a part of the engine, have a creature that constantly keeps a bubble of water around itself (drawing from nearby sources as it approaches them). When it runs up to regular creatures to try to attack them, it will force them into swimming state or outright kill them. +Ocean-faring monsters that create voids that push away liquid (thus making them challenging to fight on a boat) Scary creatures that, when entering a chunk, gradually reduce environment volume to zero (ie crickets stop chirping because it's so scary) Like a demon scarecrow or devil \ No newline at end of file diff --git a/docs/src/highlevel-design/fluid/fluidbifrication.md b/docs/src/highlevel-design/fluid/fluidbifrication.md new file mode 100644 index 00000000..1a26b56f --- /dev/null +++ b/docs/src/highlevel-design/fluid/fluidbifrication.md @@ -0,0 +1,25 @@ +@page fluidbifrication Fluid Bifrication + +Goals is to have at least two systems that perform fluid dynamics. System 1 is a full CFD. System 2 is a simpler cellular automata. + + + + +# System 1 (Computational Fluid Dynamics (CFD)) + +As this is visually and gameplay wise the most interesting, all chunks closest to players will have this calculation running. + + + + + + +# System 2 (Cellular Automata) + +@subpage fluidcellularautomata + +For chunks that are just chunkloaded or otherwise not close to the player, rely on a cellular automata approach. +This can also dynamically be used to salvage fps. If the player has a low end machine and is hosting a multiplayer world, +the engine can try to remedy that by swapping active chunks into cellular automata lane. It should end up being substantially +faster to simulate given its nature. + diff --git a/docs/src/highlevel-design/fluid/fluidcellularautomata.md b/docs/src/highlevel-design/fluid/fluidcellularautomata.md new file mode 100644 index 00000000..28eb3b83 --- /dev/null +++ b/docs/src/highlevel-design/fluid/fluidcellularautomata.md @@ -0,0 +1,11 @@ +@page fluidcellularautomata Fluid Cellular Automata + +Rules of the system: + +Advect the velocity + +Calculate where the velocity was last frame + +Move density from where it was last frame to current frame (advect density) + +Diffuse density \ No newline at end of file diff --git a/docs/src/highlevel-design/fluid/fluidchunk.md b/docs/src/highlevel-design/fluid/fluidchunk.md new file mode 100644 index 00000000..bed78524 --- /dev/null +++ b/docs/src/highlevel-design/fluid/fluidchunk.md @@ -0,0 +1,17 @@ +@page fluidchunk Fluid Chunk + +# What data it has + +Density - The amount of density at each point in the chunk + +Vector - The velocity in each dimension + +Density Addition - The amount of density to add/remove next frame + +Vector Addition - The amount of velocity to add/remove next frame + + + + +# Rendering + diff --git a/docs/src/highlevel-design/fluid/fluidindex.md b/docs/src/highlevel-design/fluid/fluidindex.md new file mode 100644 index 00000000..1d4df243 --- /dev/null +++ b/docs/src/highlevel-design/fluid/fluidindex.md @@ -0,0 +1,6 @@ +@page fluidindex Fluid Simulation + +[TOC] +- @subpage fluidbifrication +- @subpage fluidchunk +- @subpage fluidcellularautomata \ No newline at end of file diff --git a/docs/src/highlevel-design/highleveldesignindex.md b/docs/src/highlevel-design/highleveldesignindex.md index 4f6c773f..82ed8deb 100644 --- a/docs/src/highlevel-design/highleveldesignindex.md +++ b/docs/src/highlevel-design/highleveldesignindex.md @@ -12,4 +12,6 @@ Discussion of, at a high game-design level, how everything should work and conne - @subpage macrosimtimeline - @subpage narrativemanager - @subpage itemsindex -- @subpage puzzleindex \ No newline at end of file +- @subpage puzzleindex +- @subpage fluidindex +- @subpage locomotion \ No newline at end of file diff --git a/docs/src/highlevel-design/locations/largelocationideas.md b/docs/src/highlevel-design/locations/largelocationideas.md index 2db3984c..aea94c25 100644 --- a/docs/src/highlevel-design/locations/largelocationideas.md +++ b/docs/src/highlevel-design/locations/largelocationideas.md @@ -3,4 +3,5 @@ ### Natural Colosseum -### \ No newline at end of file +### "Black Hole" Zone +Zone with a big magical fire in the center or something like that. IE it's a huge pit and the zone you play in is around this pit. diff --git a/docs/src/highlevel-design/locomotion/locomotion.md b/docs/src/highlevel-design/locomotion/locomotion.md new file mode 100644 index 00000000..9e47bf0e --- /dev/null +++ b/docs/src/highlevel-design/locomotion/locomotion.md @@ -0,0 +1,22 @@ +@page locomotion Locomotion (Or how will the player get around) + + +Minimum to ship open world: + +Horses + +Gliders + +Boats + +Airships + + +Alternate forms: + +Flying mounts + +Cannons? lol + +Portals + diff --git a/docs/src/highlevel-design/narrativemanager/narrativemanager.md b/docs/src/highlevel-design/narrativemanager/narrativemanager.md index dcc6ccdd..bc206cff 100644 --- a/docs/src/highlevel-design/narrativemanager/narrativemanager.md +++ b/docs/src/highlevel-design/narrativemanager/narrativemanager.md @@ -1,5 +1,11 @@ @page narrativemanager Narrative Manager + + +[TOC] + - @subpage whatmakesaquestgood + + TODO: describe basic idea is have a system that watches over each player and tries to shape the world around them to create interesting stories. diff --git a/docs/src/highlevel-design/narrativemanager/whatmakesaquestgood.md b/docs/src/highlevel-design/narrativemanager/whatmakesaquestgood.md new file mode 100644 index 00000000..d1b4ac5d --- /dev/null +++ b/docs/src/highlevel-design/narrativemanager/whatmakesaquestgood.md @@ -0,0 +1,33 @@ +@page whatmakesaquestgood What makes a quest good + +Collection of random throughs on the subject above + + +Potentially what makes a quest good: + - character moments? + - sense of scale? + - puzzle solving? + - choice? + + - the complication of the quest or the story of the quest is what makes it good + - a character has a goal and you help them achieve it + - plot twist + - a story arc + +Good quests: +tarrey town + +Axioms to strive for: + - has to be well contextualized in world + - decisions have to have impact on the world + - maybe helps to have different gameplay** + + +Approaches for aligning with those axioms: + - have game probe for character positions on issues, then set antagonist goals in opposition to them + + + +examples: + - necromancer ethnostate - liam + diff --git a/docs/src/highlevel-design/puzzles/puzzleindex.md b/docs/src/highlevel-design/puzzles/puzzleindex.md index 3737b353..06f10e7d 100644 --- a/docs/src/highlevel-design/puzzles/puzzleindex.md +++ b/docs/src/highlevel-design/puzzles/puzzleindex.md @@ -4,4 +4,5 @@ Everything puzzles [TOC] - @subpage puzzlehighlevelflow -- @subpage puzzleideas \ No newline at end of file +- @subpage puzzleideas +- @subpage puzzlespells \ No newline at end of file diff --git a/docs/src/progress/bigthings.md b/docs/src/progress/bigthings.md new file mode 100644 index 00000000..3680cf07 --- /dev/null +++ b/docs/src/progress/bigthings.md @@ -0,0 +1,19 @@ +@page bigthings Big Things I want to build +# and may or may not have the sanity to build + + - CFD + - Internal Boundaries + - Multigrid optimization + - Network optimization + + - Transvoxel Algorithm + + - Deferred Shading Pipeline + + - Audio Ray Tracing + + - Hair/Cloth Simulation + + - Massive scale creature groups + (ie 10k armies) + diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 575ff557..55cb5487 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1,6 +1,6 @@ @page renderertodo TODO - +@subpage bigthings Big Things To Build @@ -153,20 +153,26 @@ Foliage Manager upgrades Fix arena mode (terrain be crazy) - - - -# TODO - +(03/17/2024) Optimize instance logic (currently sorting the list of objects to push to buffer each frame nukes the cpu) sort nukes cpu because capacity logic is after sort, so it tries to sort ~600k items every frame before caping The way to optimize this is to completely gut existing code. One draw call per tile. Draw call calles drawInstanced for all blades within that cell. Provide a texture that contains data per blade of grass to the cell on draw. Store positions in that once at creation. For dynamic wind later will need to use UBOs or something like that. + Fix grass flickering (it's frustum culling being inconsistent, try commenting it out in InstancedActor and see what happens :| ) (well we won't have that problem anymore lol) -Fix grass flickering (it's frustum culling being inconsistent, try commenting it out in InstancedActor and see what happens :| ) + + + +# TODO + +Cellular Automata Fluid Dynamics System + - Advect force + - Advect density + - Diffuse density Fix character movement + - Walking left or right while turning camera is very jittery Fix Frustum Culling for skybox @@ -175,15 +181,6 @@ Fix Character creation preview not working Clean up main method/class - -Fluid Dynamics System - - Basic transparent voxels for fluid - - Fluid simulation algorithm - - Vectorize/JNI fluid simulation - - Separate fluid chunks - - Networked fluid chunk transportation (including force vectors for generating particles on client) - - Save fluid chunks - Transvoxel Algorithm Client Terrain Entity Management (specifically creation/teardown for client) - Also queries for far out chunks to load far away terrain @@ -203,6 +200,14 @@ Build a lod system - Could potentially be held at actor level - Link different models based on LOD level - LOD trees aggressively + - LOD foliage cells aggressively + +Light Manager + - Creates and manages light entities + - Uses priority queue mechanism like foliage manager to only draw the most important lights + - Support "flickering" somehow + - Eventually support spot lights? + - Point shadows ??? skybox work @@ -277,12 +282,6 @@ Automatic Scene unloading - Entity decomposition - Server handling - Client handling when scene should be unloaded -Light Manager - - Creates and manages light entities - - Uses priority queue mechanism like foliage manager to only draw the most important lights - - Support "flickering" somehow - - Eventually support spot lights? - - Point shadows ??? Generic collision engine to support different instances of engine (eg hitboxes vs terrain vs liquids, etc) - Major refactoring to happen here Procedural Cliff Texture @@ -296,6 +295,13 @@ Loot Generator - System that can generate items that would be appropriate reward given some variables - ie you tell it 'this is this character's stats, this is the relative level of loot I want to provide' - it then searches through the lore to generate appropriate weapons, armor, materials, consumables, etc +Computational Fluid Dynamics System + - Basic transparent voxels for fluid + - Fluid simulation algorithm + - Vectorize/JNI fluid simulation + - Separate fluid chunks + - Networked fluid chunk transportation (including force vectors for generating particles on client) + - Save fluid chunks diff --git a/docs/src/rendering/indexdrawcell.md b/docs/src/rendering/indexrendering.md similarity index 51% rename from docs/src/rendering/indexdrawcell.md rename to docs/src/rendering/indexrendering.md index 041e6ec2..364e47ef 100644 --- a/docs/src/rendering/indexdrawcell.md +++ b/docs/src/rendering/indexrendering.md @@ -2,6 +2,8 @@ [TOC] +- @subpage actorsindex - @subpage DrawCell - @subpage Fonts -- @subpage modelLoading \ No newline at end of file +- @subpage modelLoading +- @subpage instanceindex \ No newline at end of file diff --git a/docs/src/rendering/instancing/instancingarch.md b/docs/src/rendering/instancing/instancingarch.md index 3d8459c7..fc8332b2 100644 --- a/docs/src/rendering/instancing/instancingarch.md +++ b/docs/src/rendering/instancing/instancingarch.md @@ -2,6 +2,7 @@ Idea is to have two parallel approaches to buffers that are pushed into gpu One is traditional actor architecture where you call draw on an actor and it puts just its info into InstanceData object (think close trees, rocks, etc) -Other approach is to have an object that represents a bucket of data. You call draw on the bucket and it pushes an array of info into the InstanceData (think grass) +Other approach is to have an object that represents a bucket of data. You call draw on the bucket and it pushes an array of info a texture (think grass) + - This texture is then iterated over by vertex shader Both push data into InstanceData, which is then iterated over in draw calls by instanceManager \ No newline at end of file diff --git a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java index 3bdc603b..e7702935 100644 --- a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java +++ b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java @@ -1,5 +1,8 @@ package electrosphere.client.foliagemanager; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -13,6 +16,8 @@ import org.joml.Quaterniond; import org.joml.Vector3d; import org.joml.Vector3f; import org.joml.Vector3i; +import org.lwjgl.BufferUtils; +import org.lwjgl.system.MemoryUtil; import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; @@ -23,20 +28,16 @@ import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.foliage.AmbientFoliage; import electrosphere.game.data.foliage.type.FoliageType; +import electrosphere.renderer.actor.instance.TextureInstancedActor; import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; +import electrosphere.renderer.texture.Texture; /** * Manages ambient foliage (grass, small plants, etc) that should be shown, typically instanced */ public class ClientFoliageManager { - //threshold for a grass blade to relocate to a new position - static final float GRASS_RELOCATION_THRESHOLD = 5f; - - //amount of grass entities to manage - static final int grassCapacity = 10000; - //Random for finding new positions for foliage Random placementRandomizer = new Random(); @@ -51,11 +52,28 @@ public class ClientFoliageManager { //map of position-based key to foliage cell at the position Map locationCellMap = new HashMap(); //The maximum distance a cell can be away from the player before being destroyed - static final float CELL_DISTANCE_MAX = 25f; - //The maximum number of foliage cells - static final int CELL_COUNT_MAX = 25; + static final float CELL_DISTANCE_MAX = 5f; + + //The maximum number of foliage cells + + static final int CELL_COUNT_MAX = 1000; + //the interval to space along + static final int TARGET_FOLIAGE_SPACING = 50; //The target number of foliage to place per cell - static final int TARGET_FOLIAGE_PER_CELL = 10; + static final int TARGET_FOLIAGE_PER_CELL = TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING; + //size of a single item of foliage in the texture buffer + /* + * A lot of these are x 4 to account for size of float + * 3 x 4 for position + * 2 x 4 for euler rotation + * + * + * eventually: + * grass type + * color + * wind characteristics? + */ + static final int SINGLE_FOLIAGE_DATA_SIZE_BYTES = 3 * 4 + 2 * 4; //Stores a list of all locations that are currently invalid which map to //the amount of frames that must pass before they are considered valid to evaluate Map locationEvaluationCooldownMap = new ConcurrentHashMap(); @@ -84,8 +102,8 @@ public class ClientFoliageManager { } //shader paths - static final String vertexPath = "shaders/foliage/foliage.vs"; - static final String fragmentPath = "shaders/foliage/foliage.fs"; + static final String vertexPath = "Shaders/foliage/foliage.vs"; + static final String fragmentPath = "Shaders/foliage/foliage.fs"; @@ -106,6 +124,7 @@ public class ClientFoliageManager { for(FoliageType foliageType : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){ if(foliageType.getTokens().contains(FoliageType.TOKEN_AMBIENT)){ Globals.assetManager.addModelPathToQueue(foliageType.getModelPath()); + Globals.assetManager.addShaderToQueue(vertexPath, fragmentPath); } } ready = true; @@ -116,19 +135,6 @@ public class ClientFoliageManager { */ public void update(){ if(ready){ - //TODO: frustum cull at cell level before individual model level - //to be clear, these blades are frustum culled 1-by-1 currently at the InstancedActor level - //if we frustum cull at cell level with priority updates around then, we can pack foliage into buffer in chunks - //and maintain the size of chunks and location on cpu - //then use opengl calls to buffer only occasionally - //if we're doing that we can also increase the amount and type of data (would be really nice to include position-in-chunk of foliage - //for instance for fancy shaders based on local position (think rainbow grass constantly pulsing)) - //this will inherit all the difficulty of defragmenting the buffer, turning individual chunks on and off on gpu side, etc - //a way to save on sending position buffer to gpu would be to pack voxel pos into a smaller size ie use bitwise operators to store - //local coord in 1 * 4 bytes instead of 3 * 4 for a full vector - for(FoliageCell cell : activeCells){ - cell.draw(modelMatrixAttribute); - } //for each invalid cell, see if can be revalidated for(String key : locationEvaluationCooldownMap.keySet()){ int cooldownTime = locationEvaluationCooldownMap.get(key); @@ -164,7 +170,7 @@ public class ClientFoliageManager { */ protected Vector3d getNewPosition(Vector3d centerPosition){ double angle = placementRandomizer.nextDouble() * Math.PI * 2; - double radius = placementRandomizer.nextDouble() * GRASS_RELOCATION_THRESHOLD; + double radius = placementRandomizer.nextDouble(); return new Vector3d( centerPosition.x + Math.cos(angle) * radius, centerPosition.y, @@ -177,7 +183,7 @@ public class ClientFoliageManager { * @return The rotation */ protected Quaterniond getNewRotation(){ - return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat()); + return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat()).normalize(); } /** @@ -197,7 +203,7 @@ public class ClientFoliageManager { * @param modelPath The model path for the model to back the instanced actor * @param capacity The capacity of the instanced actor to draw */ - public static void makeEntityInstancedFoliage(Entity entity, String modelPath, int capacity){ + public static void makeEntityTextureInstancedFoliage(Entity entity, String modelPath, int capacity){ entity.putData(EntityDataStrings.INSTANCED_ACTOR, Globals.clientInstanceManager.createInstancedActor(modelPath, vertexPath, fragmentPath, attributes, capacity)); entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0)); entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity()); @@ -242,7 +248,8 @@ public class ClientFoliageManager { !locationEvaluationCooldownMap.containsKey(key) && data.getWeight(currentPos) > 0 && data.getWeight(new Vector3i(x,y + 1,z)) < 0 && - typeSupportsFoliage(data.getType(currentPos)) + typeSupportsFoliage(data.getType(currentPos)) && + activeCells.size() < CELL_COUNT_MAX ){ //create foliage cell createFoliageCell(worldPos,currentPos,1); @@ -280,7 +287,8 @@ public class ClientFoliageManager { if( data.getWeight(currentPos) > 0 && aboveData.getWeight(new Vector3i(x,0,z)) < 0 && - typeSupportsFoliage(data.getType(currentPos)) + typeSupportsFoliage(data.getType(currentPos)) && + activeCells.size() < CELL_COUNT_MAX ){ //create foliage cell createFoliageCell(worldPos,currentPos,1); @@ -291,12 +299,19 @@ public class ClientFoliageManager { } } + //the length of the ray to ground test with + static final float RAY_LENGTH = 2.0f; + + //the height above the chunk to start from when sampling downwards + static final float SAMPLE_START_HEIGHT = 1.0f; + /** * Creates a foliage cell at a given position * @param worldPos The world position * @param voxelPos The voxel position */ private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel){ + System.out.println("Create foliage cell"); //get foliage types supported ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); List foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPos)).getAmbientFoliage(); @@ -306,37 +321,157 @@ public class ClientFoliageManager { worldPos.y * ChunkData.CHUNK_SIZE + voxelPos.y, worldPos.z * ChunkData.CHUNK_SIZE + voxelPos.z ); + + //get type + String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size()); + FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName); + + //create cell and buffer FoliageCell cell = new FoliageCell(worldPos, voxelPos, realPos); - //create center foliage - for(int i = 0; i < TARGET_FOLIAGE_PER_CELL; i++){ - //get type - String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size()); - FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName); - //get position to place - double offsetX = placementRandomizer.nextDouble() - 0.5; - double offsetY = 0; - double offsetZ = placementRandomizer.nextDouble() - 0.5; - // double offsetY = placeFoliage(dataToConsider, offsetX, offsetZ, 0.2, 0.5, 0.15, 0.2+0.6); - Vector3d testPosition = new Vector3d( - worldPos.x * ChunkData.CHUNK_SIZE + voxelPos.x + offsetX, - worldPos.y * ChunkData.CHUNK_SIZE + voxelPos.y + offsetY, - worldPos.z * ChunkData.CHUNK_SIZE + voxelPos.z + offsetZ - ); - // Vector3d placementPos = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(testPosition, new Vector3d(0,-1,0), 2.5); - if(testPosition != null){ - //create entity - Entity grassEntity = EntityCreationUtils.createClientSpatialEntity(); - makeEntityInstancedFoliage(grassEntity, foliageType.getModelPath(), grassCapacity); - EntityUtils.getPosition(grassEntity).set(testPosition); - EntityUtils.getRotation(grassEntity).set(getNewRotation()); - EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0)); - //add ambient foliage behavior tree - AmbientFoliage.attachAmbientFoliageTree(grassEntity, initialGrowthLevel, foliageType.getGrowthModel().getGrowthRate()); - cell.addEntity(grassEntity); - } + ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES); + FloatBuffer floatBufferView = buffer.asFloatBuffer(); + //construct simple grid to place foliage on + Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add(-0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_01 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add(-0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_02 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add(-0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_10 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_11 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_12 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_20 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_21 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); + Vector3d sample_22 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add( 0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); + //get the heights of each sample + float height_11 = (float)(sample_11 != null ? sample_11.y : 0); + float height_00 = (float)(sample_00 != null ? sample_00.y : height_11); + float height_01 = (float)(sample_01 != null ? sample_01.y : height_11); + float height_02 = (float)(sample_02 != null ? sample_02.y : height_11); + float height_10 = (float)(sample_10 != null ? sample_10.y : height_11); + float height_12 = (float)(sample_12 != null ? sample_12.y : height_11); + float height_20 = (float)(sample_20 != null ? sample_20.y : height_11); + float height_21 = (float)(sample_21 != null ? sample_21.y : height_11); + float height_22 = (float)(sample_22 != null ? sample_22.y : height_11); + if(worldPos.x == 0 && worldPos.z == 0 && voxelPos.x < 5 && voxelPos.z < 5){ + System.out.println(voxelPos); + System.out.println(height_11); + System.out.println(realPos); + } + //each height is in real world coordinates that are absolute + //when rendering, there's already a y offset for the center of the field of grass (based on the model matrix) + //so when offseting the position of the blade of grass RELATIVE to the overall instance being drawn, need to subtract the real world coordinates of the overall instance + //in other words realPos SPECIFICALLY for the y dimension, for x and z you don't need to worry about it + + //if we don't find data for the center sample, can't place grass so don't create entity + if(sample_11 != null){ + //generate positions to place + int drawCount = 0; + for(int x = 0; x < TARGET_FOLIAGE_SPACING; x++){ + for(int z = 0; z < TARGET_FOLIAGE_SPACING; z++){ + //get position to place + double rand1 = placementRandomizer.nextDouble(); + double rand2 = placementRandomizer.nextDouble(); + double relativePositionOnGridX = x / (1.0 * TARGET_FOLIAGE_SPACING) - 0.5 + rand1 / TARGET_FOLIAGE_SPACING; + double relativePositionOnGridZ = z / (1.0 * TARGET_FOLIAGE_SPACING) - 0.5 + rand2 / TARGET_FOLIAGE_SPACING; + double offsetX = relativePositionOnGridX; + double offsetZ = relativePositionOnGridZ; + //determine quadrant we're placing in + double offsetY = 0; + if(worldPos.x == 0 && worldPos.z == 0 && voxelPos.x < 5 && voxelPos.z < 5 && x == 26 && z == 26){ + offsetY = + height_11 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_12 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) + + height_21 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_22 * ( relativePositionOnGridX) * ( relativePositionOnGridZ); + System.out.println(offsetY); + System.out.println((float)offsetY - (float)realPos.y); + } + boolean addBlade = false; + if(relativePositionOnGridX >=0){ + if(relativePositionOnGridZ >= 0){ + relativePositionOnGridX += 0.5; + relativePositionOnGridZ += 0.5; + //if we have heights for all four surrounding spots, interpolate for y value + if(sample_11 != null && sample_12 != null && sample_21 != null && sample_22 != null){ + offsetY = + height_11 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_12 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) + + height_21 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_22 * ( relativePositionOnGridX) * ( relativePositionOnGridZ); + addBlade = true; + } + } else { + relativePositionOnGridX += 0.5; + relativePositionOnGridZ += 0.5; + //if we have heights for all four surrounding spots, interpolate for y value + if(sample_10 != null && sample_11 != null && sample_20 != null && sample_21 != null){ + offsetY = + height_10 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_11 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) + + height_20 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_21 * ( relativePositionOnGridX) * ( relativePositionOnGridZ); + addBlade = true; + } + } + } else { + if(relativePositionOnGridZ >= 0){ + relativePositionOnGridX += 0.5; + relativePositionOnGridZ += 0.5; + //if we have heights for all four surrounding spots, interpolate for y value + if(sample_01 != null && sample_02 != null && sample_11 != null && sample_12 != null){ + offsetY = + height_01 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_02 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) + + height_11 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_12 * ( relativePositionOnGridX) * ( relativePositionOnGridZ); + addBlade = true; + } + } else { + relativePositionOnGridX += 0.5; + relativePositionOnGridZ += 0.5; + //if we have heights for all four surrounding spots, interpolate for y value + if(sample_00 != null && sample_01 != null && sample_10 != null && sample_11 != null){ + offsetY = + height_00 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_01 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) + + height_10 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) + + height_11 * ( relativePositionOnGridX) * ( relativePositionOnGridZ); + addBlade = true; + } + } + } + if(addBlade){ + //convert y to relative to chunk + offsetY = offsetY - realPos.y; + double rotVar = placementRandomizer.nextDouble() * Math.PI * 2; + double rotVar2 = placementRandomizer.nextDouble(); + floatBufferView.put((float)offsetX); + floatBufferView.put((float)offsetY); + floatBufferView.put((float)offsetZ); + floatBufferView.put((float)rotVar); + floatBufferView.put((float)rotVar2); + drawCount++; + } + } + } + buffer.position(0); + buffer.limit(TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES); + //construct data texture + Texture dataTexture = new Texture(buffer,SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4,TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING); + + //create entity + Entity grassEntity = EntityCreationUtils.createClientSpatialEntity(); + + TextureInstancedActor.attachTextureInstancedActor(grassEntity, foliageType.getModelPath(), vertexPath, fragmentPath, dataTexture, drawCount); + EntityUtils.getPosition(grassEntity).set(realPos); + EntityUtils.getRotation(grassEntity).set(0,0,0,1); + EntityUtils.getScale(grassEntity).set(new Vector3d(4.0, 4.0, 4.0)); + //add ambient foliage behavior tree + AmbientFoliage.attachAmbientFoliageTree(grassEntity, initialGrowthLevel, foliageType.getGrowthModel().getGrowthRate()); + cell.addEntity(grassEntity); + + + activeCells.add(cell); + locationCellMap.put(getFoliageCellKey(worldPos, voxelPos),cell); } - activeCells.add(cell); - locationCellMap.put(getFoliageCellKey(worldPos, voxelPos),cell); } } @@ -387,6 +522,15 @@ public class ClientFoliageManager { } } + /** + * Draws all foliage in the foliage manager + */ + public void draw(){ + for(FoliageCell cell : activeCells){ + cell.draw(modelMatrixAttribute); + } + } + } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 5cff7b1c..5e3f96c2 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -16,8 +16,10 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.foliage.AmbientFoliage; import electrosphere.entity.types.camera.CameraEntityUtils; +import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.actor.instance.InstancedActor; +import electrosphere.renderer.actor.instance.TextureInstancedActor; import electrosphere.renderer.buffer.ShaderAttribute; /** @@ -80,12 +82,13 @@ public class FoliageCell { protected void draw(ShaderAttribute modelMatrixAttribute){ Matrix4d modelMatrix = new Matrix4d(); Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera); - Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity); RenderPipelineState renderPipelineState = Globals.renderingEngine.getRenderPipelineState(); + OpenGLState openGLState = Globals.renderingEngine.getOpenGLState(); + Vector3f cameraModifiedPosition = new Vector3f((float)realPosition.x,(float)realPosition.y,(float)realPosition.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); //frustum check entire cell - boolean shouldRender = renderPipelineState.getFrustumIntersection().testSphere((float)(realPosition.x + boundingSphere.x), (float)(realPosition.y + boundingSphere.y), (float)(realPosition.z + boundingSphere.z), (float)(boundingSphere.r)); + boolean shouldRender = renderPipelineState.getFrustumIntersection().testSphere((float)(cameraModifiedPosition.x + boundingSphere.x), (float)(cameraModifiedPosition.y + boundingSphere.y), (float)(cameraModifiedPosition.z + boundingSphere.z), (float)(boundingSphere.r)); if(shouldRender){ //disable frustum check and instead perform at cell level boolean currentFrustumCheckState = renderPipelineState.shouldFrustumCheck(); @@ -93,21 +96,19 @@ public class FoliageCell { for(Entity entity : containedEntities){ Vector3d grassPosition = EntityUtils.getPosition(entity); Quaterniond grassRotation = EntityUtils.getRotation(entity); - InstancedActor instancedActor = InstancedActor.getInstancedActor(entity); + TextureInstancedActor actor = TextureInstancedActor.getTextureInstancedActor(entity); + modelMatrix = modelMatrix.identity(); - Vector3f cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter); + cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter); modelMatrix.translate(cameraModifiedPosition); modelMatrix.rotate(new Quaterniond(grassRotation)); modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity))); + actor.applyModelMatrix(modelMatrix); - instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f(modelMatrix)); - - //set priority equal to distance - instancedActor.setPriority((int)grassPosition.distance(playerPosition)); //draw - instancedActor.draw(renderPipelineState, new Vector3d(0,0,0)); + actor.draw(renderPipelineState, openGLState); } renderPipelineState.setFrustumCheck(currentFrustumCheckState); } diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index a85bbd05..d81947c0 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -23,6 +23,7 @@ public class EntityDataStrings { public static final String DRAW_OUTLINE = "drawOutline"; public static final String INSTANCED_ACTOR = "instancedActor"; public static final String DRAW_INSTANCED = "drawInstanced"; + public static final String TEXTURE_INSTANCED_ACTOR = "textureInstancedActor"; /* diff --git a/src/main/java/electrosphere/renderer/RenderPipelineState.java b/src/main/java/electrosphere/renderer/RenderPipelineState.java index 0d5741ff..caee8d89 100644 --- a/src/main/java/electrosphere/renderer/RenderPipelineState.java +++ b/src/main/java/electrosphere/renderer/RenderPipelineState.java @@ -53,6 +53,9 @@ public class RenderPipelineState { boolean instanced = false; //The instance data for rendering an instanced object InstanceData instanceData; + //the number of instances to draw + int instanceCount = 0; + //The pointer to the current shader program bound int currentShaderPointer; @@ -142,6 +145,14 @@ public class RenderPipelineState { this.instanceData = instanceData; } + public void setInstanceCount(int count){ + this.instanceCount = count; + } + + public int getInstanceCount(){ + return this.instanceCount; + } + public int getCurrentShaderPointer(){ return currentShaderPointer; } diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index c0dd927a..204b5e32 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -17,12 +17,10 @@ import org.joml.AxisAngle4f; import org.joml.Matrix4d; import org.joml.Matrix4f; import org.joml.Quaterniond; -import org.joml.Quaternionf; import org.joml.Sphered; import org.joml.Vector3d; import org.joml.Vector3f; import org.joml.Vector4d; -import org.joml.Vector4f; /** * An actor diff --git a/src/main/java/electrosphere/renderer/actor/instance/TextureInstancedActor.java b/src/main/java/electrosphere/renderer/actor/instance/TextureInstancedActor.java new file mode 100644 index 00000000..2912a317 --- /dev/null +++ b/src/main/java/electrosphere/renderer/actor/instance/TextureInstancedActor.java @@ -0,0 +1,121 @@ +package electrosphere.renderer.actor.instance; + +import org.joml.Matrix4d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.model.Material; +import electrosphere.renderer.model.Model; +import electrosphere.renderer.shader.ShaderProgram; +import electrosphere.renderer.texture.Texture; + +/** + * An actor that will trigger an instance call when you draw the model; however it uses a texture to store data about its instances + */ +public class TextureInstancedActor { + + //path of the model that this instanced actor uses + String modelPath; + + //the material that will contain the data about the model + Material material; + + //the draw count of the texture instanced actor + int drawCount; + + //shader paths + String vertexShaderPath; + String fragmentShaderPath; + + /** + * Creates an instanced actor + * @param modelPath The path of the model this actor uses + */ + protected TextureInstancedActor(String modelPath, String vertexShaderPath, String fragmentShaderPath, Texture dataTexture, int drawCount){ + this.modelPath = modelPath; + this.material = new Material(); + this.material.setTexturePointer(dataTexture.getTexturePointer()); + this.drawCount = drawCount; + this.vertexShaderPath = vertexShaderPath; + this.fragmentShaderPath = fragmentShaderPath; + } + + /** + * Attaches a TextureInstancedActor to an entity + * @param parent The entity + * @param modelPath The path to the model for this instanced actor + * @param dataTexture The data texture containing data for this actor + */ + public static void attachTextureInstancedActor(Entity parent, String modelPath, String vertexShaderPath, String fragmentShaderPath, Texture dataTexture, int drawCount){ + TextureInstancedActor newActor = new TextureInstancedActor(modelPath, vertexShaderPath, fragmentShaderPath, dataTexture, drawCount); + parent.putData(EntityDataStrings.TEXTURE_INSTANCED_ACTOR, newActor); + } + + /** + * Draws the instanced actor. Should be called normally in a loop as if this was a regular actor. + * @param renderPipelineState The pipeline state of the instanced actor + * @param position The position used for frustum checking + */ + public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){ + Model model = Globals.assetManager.fetchModel(modelPath); + ShaderProgram shader = Globals.assetManager.fetchShader(vertexShaderPath, null, fragmentShaderPath); + if(model != null && shader != null){ + //setup render pipeline + boolean instancedState = renderPipelineState.getInstanced(); + boolean materialState = renderPipelineState.getUseMaterial(); + boolean useShader = renderPipelineState.getUseMeshShader(); + boolean bufferStandardUniforms = renderPipelineState.getBufferStandardUniforms(); + boolean useLight = renderPipelineState.getUseLight(); + renderPipelineState.setInstanced(true); + renderPipelineState.setUseMaterial(false); + renderPipelineState.setUseMeshShader(false); + renderPipelineState.setInstanceCount(drawCount); + renderPipelineState.setBufferStandardUniforms(true); + renderPipelineState.setUseLight(true); + renderPipelineState.setInstanceData(null); //need to set the instance data to null otherwise it will overwrite what we currently have set (ie overwrite draw calls count, etc) + + + openGLState.setActiveShader(renderPipelineState, shader); + this.material.apply_material(openGLState); + model.draw(renderPipelineState, openGLState); + + //reset render pipeline state + renderPipelineState.setInstanced(instancedState); + renderPipelineState.setUseMaterial(materialState); + renderPipelineState.setUseMeshShader(useShader); + renderPipelineState.setBufferStandardUniforms(bufferStandardUniforms); + renderPipelineState.setUseLight(useLight); + } + } + + /** + * Gets the path of the model packing this instanced actore + * @return The path of the model + */ + protected String getModelPath(){ + return this.modelPath; + } + + /** + * Gets the texture instanced actor attached to this entity + * @param parent The parent entity + * @return The texture instanced actor if it exists + */ + public static TextureInstancedActor getTextureInstancedActor(Entity parent){ + return (TextureInstancedActor)parent.getData(EntityDataStrings.TEXTURE_INSTANCED_ACTOR); + } + + /** + * Applies the model matrix to the model underlying this textured instanced actor + * @param modelMatrix The model matrix + */ + public void applyModelMatrix(Matrix4d modelMatrix){ + Model model = Globals.assetManager.fetchModel(modelPath); + if(model != null){ + model.setModelMatrix(modelMatrix); + } + } +} diff --git a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java index 2975d557..dd7eeb3f 100644 --- a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java +++ b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java @@ -72,9 +72,11 @@ public class MeshLoader { Matrix4d vertexPretransform = new Matrix4d().identity(); + Matrix4d normalPretransform = new Matrix4d().identity(); if(metadata != null){ LoggerInterface.loggerRenderer.DEBUG("Pretransforming"); vertexPretransform.translationRotateScale(metadata.getOffset(), metadata.getRotation(), metadata.getScale()); + normalPretransform.rotate(metadata.getRotation()); } // @@ -139,9 +141,10 @@ public class MeshLoader { float[] temp = new float[3]; for (int i = 0; i < normalCount; i++) { AIVector3D normal = normals.get(i); - temp[0] = normal.x(); - temp[1] = normal.y(); - temp[2] = normal.z(); + Vector4d transformedNormal = normalPretransform.transform(new Vector4d(normal.x(),normal.y(),normal.z(),1.0)); + temp[0] = (float)transformedNormal.x(); + temp[1] = (float)transformedNormal.y(); + temp[2] = (float)transformedNormal.z(); NormalArrayBufferData.put(temp); } NormalArrayBufferData.flip(); diff --git a/src/main/java/electrosphere/renderer/model/Mesh.java b/src/main/java/electrosphere/renderer/model/Mesh.java index b599c33b..8da0d8bb 100644 --- a/src/main/java/electrosphere/renderer/model/Mesh.java +++ b/src/main/java/electrosphere/renderer/model/Mesh.java @@ -447,15 +447,18 @@ public class Mesh { } if(renderPipelineState.getInstanced()){ - InstanceData instanceData = renderPipelineState.getInstanceData(); - Map buffers = instanceData.getCpuBufferMap(); - Map glBufferMap = instanceData.getGlBufferMap(); - bufferInstanceData(renderPipelineState, buffers, glBufferMap); + if(renderPipelineState.getInstanceData()!=null){ + InstanceData instanceData = renderPipelineState.getInstanceData(); + Map buffers = instanceData.getCpuBufferMap(); + Map glBufferMap = instanceData.getGlBufferMap(); + bufferInstanceData(renderPipelineState, buffers, glBufferMap); + renderPipelineState.setInstanceCount(instanceData.getDrawCount()); + } } if(renderPipelineState.getInstanced()){ - GL45.glDrawElementsInstanced(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0, renderPipelineState.getInstanceData().getDrawCount()); + GL45.glDrawElementsInstanced(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0, renderPipelineState.getInstanceCount()); } else { GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0); } diff --git a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java index db561da6..ccc89af6 100644 --- a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java @@ -87,6 +87,7 @@ public class MainContentPipeline implements RenderPipeline { currentActor.draw(renderPipelineState,openGLState); } } + Globals.clientFoliageManager.draw(); for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){ Vector3d position = EntityUtils.getPosition(currentEntity); if( diff --git a/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java b/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java index c677a36a..b1ff6f50 100644 --- a/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java @@ -75,7 +75,7 @@ public class ShadowMapPipeline implements RenderPipeline { for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){ Vector3d position = EntityUtils.getPosition(currentEntity); if( - (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && + currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)!=null && currentEntity.containsKey(EntityDataStrings.DRAW_CAST_SHADOW) ){ //fetch actor diff --git a/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java b/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java index fa876d03..3647c03b 100644 --- a/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java @@ -74,7 +74,7 @@ public class VolumeBufferPipeline implements RenderPipeline { for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){ Vector3d position = EntityUtils.getPosition(currentEntity); if( - (boolean)currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && + currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)!=null && currentEntity.containsKey(EntityDataStrings.DRAW_VOLUMETRIC) ){ //fetch actor diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java index f7166ca3..cf2c9b18 100644 --- a/src/main/java/electrosphere/renderer/texture/Texture.java +++ b/src/main/java/electrosphere/renderer/texture/Texture.java @@ -201,6 +201,31 @@ public class Texture { // System.gc(); } } + + /** + * Generates a texture based on a buffer (for use passing data to gpu) + * @param buffer The buffer of data + * @param width the 'width' of the 'texture' + * @param height the 'height' of the 'texture' + */ + public Texture(ByteBuffer buffer, int width, int height){ + if(!Globals.HEADLESS){ + //generate the texture object on gpu + texturePointer = glGenTextures(); + //bind the new texture + glBindTexture(GL_TEXTURE_2D, texturePointer); + //how are we gonna wrap the texture?? + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + //disable mipmap + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //call if width != height so opengl figures out how to unpack it properly + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + //GL_RED = 32bit r value + //buffer the texture information + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, buffer); + } + } public void bind(OpenGLState openGLState){ // openGLState.glActiveTexture(GL_TEXTURE0);