Compare commits
9 Commits
41f2675e43
...
58d1d86262
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58d1d86262 | ||
|
|
512998eb24 | ||
|
|
86edabf4de | ||
|
|
077cffa2e2 | ||
|
|
db8ad7d10a | ||
|
|
bfd7690403 | ||
|
|
7ab0debc45 | ||
|
|
97edfacd41 | ||
|
|
f328d16bab |
@ -29,7 +29,7 @@
|
||||
"growthModel": {
|
||||
"growthRate" : 0.001
|
||||
},
|
||||
"modelPath" : "Models/grass1.fbx"
|
||||
"modelPath" : "Models/grass2.fbx"
|
||||
},
|
||||
{
|
||||
"name" : "oak",
|
||||
|
||||
BIN
assets/Models/grass2.fbx
Normal file
BIN
assets/Models/grass2.fbx
Normal file
Binary file not shown.
@ -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]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
208
assets/Shaders/fluid2/fluid2.fs
Normal file
208
assets/Shaders/fluid2/fluid2.fs
Normal file
@ -0,0 +1,208 @@
|
||||
#version 330 core
|
||||
|
||||
#define NR_POINT_LIGHTS 10
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
||||
layout (std140) uniform Lights {
|
||||
// this is how many because we have to align
|
||||
// bytes it SHOULD in multiples of 16, this
|
||||
// take it where it ACTUALLY is
|
||||
//
|
||||
//refer: https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
|
||||
//
|
||||
// base alignment aligned offset
|
||||
//direct light
|
||||
vec3 dLDirection; // 16 0
|
||||
vec3 dLAmbient; // 16 16
|
||||
vec3 dLDiffuse; // 16 32
|
||||
vec3 dLSpecular; // 16 48
|
||||
|
||||
//point light
|
||||
vec3 pLposition[NR_POINT_LIGHTS]; // 16*10 64
|
||||
float pLconstant[NR_POINT_LIGHTS]; // 16*10 224
|
||||
float pLlinear[NR_POINT_LIGHTS]; // 16*10 384
|
||||
float pLquadratic[NR_POINT_LIGHTS]; // 16*10 544
|
||||
vec3 pLambient[NR_POINT_LIGHTS]; // 16*10 704
|
||||
vec3 pLdiffuse[NR_POINT_LIGHTS]; // 16*10 864
|
||||
vec3 pLspecular[NR_POINT_LIGHTS]; // 16*10 1024
|
||||
|
||||
//for a total size of 1184
|
||||
|
||||
};
|
||||
|
||||
struct Material {
|
||||
sampler2D diffuse;
|
||||
sampler2D specular;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
in vec3 FragPos;
|
||||
in vec3 Normal;
|
||||
in vec2 texPlane1;
|
||||
in vec2 texPlane2;
|
||||
in vec2 texPlane3;
|
||||
in vec4 FragPosLightSpace;
|
||||
|
||||
|
||||
uniform vec3 viewPos;
|
||||
// uniform DirLight dirLight;
|
||||
// uniform PointLight pointLights[NR_POINT_LIGHTS];
|
||||
// uniform SpotLight spotLight;
|
||||
uniform Material material;
|
||||
|
||||
//texture stuff
|
||||
// uniform sampler2D ourTexture;
|
||||
uniform int hasTransparency;
|
||||
// uniform sampler2D specularTexture;
|
||||
|
||||
//light depth map
|
||||
uniform sampler2D shadowMap;
|
||||
|
||||
|
||||
// function prototypes
|
||||
// vec3 CalcDirLight(vec3 normal, vec3 viewDir);
|
||||
// vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir);
|
||||
// vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir);
|
||||
float calcLightIntensityTotal(vec3 normal);
|
||||
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, Material material);
|
||||
|
||||
void main(){
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 viewDir = normalize(viewPos - FragPos);
|
||||
|
||||
//grab light intensity
|
||||
float lightIntensity = calcLightIntensityTotal(norm);
|
||||
|
||||
//get color of base texture
|
||||
vec3 textureColor = vec3(0.6, 0.92, 0.92);//getColor(texPlane1, texPlane2, texPlane3, norm, material);
|
||||
|
||||
//shadow
|
||||
float shadow = ShadowCalculation(FragPosLightSpace, normalize(-dLDirection), norm);
|
||||
|
||||
//calculate final color
|
||||
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, 0.2);
|
||||
}
|
||||
|
||||
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, Material material){
|
||||
|
||||
vec3 weights = abs(normal);
|
||||
|
||||
vec3 albedoX = texture(material.diffuse, texPlane1).rgb;
|
||||
vec3 albedoY = texture(material.diffuse, texPlane2).rgb;
|
||||
vec3 albedoZ = texture(material.diffuse, texPlane3).rgb;
|
||||
|
||||
|
||||
return (albedoX * weights.x + albedoY * weights.y + albedoZ * weights.z);
|
||||
}
|
||||
|
||||
//
|
||||
float calcLightIntensityAmbient(){
|
||||
//calculate average of ambient light
|
||||
float avg = (dLAmbient.x + dLAmbient.y + dLAmbient.z)/3.0;
|
||||
return avg;
|
||||
}
|
||||
|
||||
//
|
||||
float calcLightIntensityDir(vec3 normal){
|
||||
vec3 lightDir = normalize(-dLDirection);
|
||||
// diffuse shading
|
||||
float diff = max(dot(normal, lightDir), 0.0);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
//
|
||||
float calcLightIntensityTotal(vec3 normal){
|
||||
//ambient intensity
|
||||
float ambientLightIntensity = calcLightIntensityAmbient();
|
||||
|
||||
//get direct intensity
|
||||
float directLightIntensity = calcLightIntensityDir(normal);
|
||||
|
||||
//sum
|
||||
float total = ambientLightIntensity + directLightIntensity;
|
||||
return total;
|
||||
}
|
||||
|
||||
//
|
||||
vec3 getTotalLightColor(vec3 normal){
|
||||
//get the direct light color adjusted for intensity
|
||||
vec3 diffuseLightColor = dLDiffuse * calcLightIntensityDir(normal);
|
||||
|
||||
//sum light colors
|
||||
vec3 totalLightColor = diffuseLightColor;
|
||||
return totalLightColor;
|
||||
}
|
||||
|
||||
vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir){
|
||||
vec3 lightDir = normalize(pLposition[i] - fragPos);
|
||||
// diffuse shading
|
||||
float diff = max(dot(normal, lightDir), 0.0);
|
||||
// specular shading
|
||||
// vec3 reflectDir = reflect(-lightDir, normal);
|
||||
// float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
|
||||
// attenuation
|
||||
float distance = length(pLposition[i] - fragPos);
|
||||
float attenuation = 1.0 / (pLconstant[i] + pLlinear[i] * distance + pLquadratic[i] * (distance * distance));
|
||||
// combine results
|
||||
vec3 ambient = pLambient[i];
|
||||
vec3 diffuse = pLdiffuse[i] * diff;
|
||||
ambient *= attenuation;
|
||||
diffuse *= attenuation;
|
||||
// specular *= attenuation;
|
||||
vec3 specular = vec3(0,0,0);
|
||||
|
||||
vec3 finalValue = (ambient + diffuse + specular);
|
||||
finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0));
|
||||
|
||||
return finalValue;
|
||||
}
|
||||
|
||||
|
||||
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){
|
||||
|
||||
// perform perspective divide
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
|
||||
//transform to NDC
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
//get closest depth from light's POV
|
||||
float closestDepth = texture(shadowMap, projCoords.xy).r;
|
||||
|
||||
//get depth of current fragment
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
//calculate bias
|
||||
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
|
||||
|
||||
//calculate shadow value
|
||||
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
|
||||
|
||||
if(projCoords.z > 1.0){
|
||||
shadow = 0.0;
|
||||
}
|
||||
|
||||
//calculate dot product, if it is >0 we know they're parallel-ish therefore should disregard the shadow mapping
|
||||
//ie the fragment is already facing away from the light source
|
||||
float dotprod = dot(normalize(lightDir),normalize(normal));
|
||||
|
||||
if(dotprod > 0.0){
|
||||
shadow = 0.0;
|
||||
}
|
||||
|
||||
// shadow = currentDepth;
|
||||
|
||||
return shadow;
|
||||
}
|
||||
62
assets/Shaders/fluid2/fluid2.vs
Normal file
62
assets/Shaders/fluid2/fluid2.vs
Normal file
@ -0,0 +1,62 @@
|
||||
//Vertex Shader
|
||||
#version 330 core
|
||||
|
||||
//defines
|
||||
#define TEXTURE_MAP_SCALE 3.0
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 4) in vec2 aTex;
|
||||
|
||||
|
||||
//coordinate space transformation matrices
|
||||
uniform mat4 transform;
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
|
||||
|
||||
//output buffers
|
||||
out vec3 Normal;
|
||||
out vec3 FragPos;
|
||||
out vec2 texPlane1;
|
||||
out vec2 texPlane2;
|
||||
out vec2 texPlane3;
|
||||
out vec4 FragPosLightSpace;
|
||||
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
//normalize posiiton and normal
|
||||
vec4 FinalVertex = vec4(aPos, 1.0);
|
||||
vec4 FinalNormal = vec4(aNormal, 1.0);
|
||||
|
||||
|
||||
//push frag, normal, and texture positions to fragment shader
|
||||
FragPos = vec3(model * FinalVertex);
|
||||
Normal = mat3(transpose(inverse(model))) * aNormal;
|
||||
|
||||
//reference https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/
|
||||
texPlane1 = aPos.zy * TEXTURE_MAP_SCALE;
|
||||
texPlane2 = aPos.xz * TEXTURE_MAP_SCALE;
|
||||
texPlane3 = aPos.xy * TEXTURE_MAP_SCALE;
|
||||
|
||||
//flip first coordinate if the normal is negative
|
||||
//this minimizes texture flipping
|
||||
texPlane1.x = texPlane1.x * sign(Normal.x);
|
||||
texPlane2.x = texPlane2.x * sign(Normal.y);
|
||||
texPlane3.x = texPlane3.x * sign(Normal.z);
|
||||
|
||||
|
||||
//shadow map stuff
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
||||
|
||||
//set final position with opengl space
|
||||
gl_Position = projection * view * model * FinalVertex;
|
||||
}
|
||||
@ -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.
|
||||
@ -236,3 +255,12 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
|
||||
float easeIn(float interpolator){
|
||||
return interpolator * interpolator;
|
||||
}
|
||||
|
||||
float easeOut(float interpolator){
|
||||
return 1 - easeIn(1 - interpolator);
|
||||
}
|
||||
@ -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 ////////////////////////////////
|
||||
5
assets/Shaders/shaderlib/shaderlib.glsl
Normal file
5
assets/Shaders/shaderlib/shaderlib.glsl
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
//courtesy https://stackoverflow.com/a/4275343
|
||||
float rand(vec2 co){
|
||||
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Tue Mar 12 22:56:01 EDT 2024
|
||||
buildNumber=47
|
||||
#Tue Mar 19 19:54:15 EDT 2024
|
||||
buildNumber=77
|
||||
|
||||
7
docs/src/architecture/archimprovementtargets.md
Normal file
7
docs/src/architecture/archimprovementtargets.md
Normal file
@ -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.
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
- @subpage uiarch
|
||||
- @subpage audioengine
|
||||
- @subpage timekeeper
|
||||
- @subpage archimprovementtargets
|
||||
|
||||
|
||||
# What is this section
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
@page fluidsimarchoverview Fluid Simulation Architecture Overview
|
||||
|
||||
# Summary of Parts
|
||||
The fluid sim begins with a function to create a running threadpool for calculations.
|
||||
Once the current server frame gets to the point that it should start calculating the fluid frame (we want to put this at the beginning of the server frame if possible), it calls a function that propagates all current fluid chunks to the threadpool.
|
||||
The threadpool then distributes the chunks amongst the threads and performs simulation for a frame.
|
||||
This simulation periodically has to synchronize data between the chunks.
|
||||
The fluid system is structured very similar to the terrain manager.
|
||||
|
||||
Make sure to explain:
|
||||
setting iso levels based on neighbor levels
|
||||
Client side interpreting 0 as -1 when receiving chunk from network (for rendering purposes)
|
||||
Overwriting same cache value on client side
|
||||
client side chunk destroying old model incl from asset manager on update
|
||||
Generic fluid simulator and specifically cellular automata ver
|
||||
How to add a fluid generator/destructor via the fluid simulator
|
||||
Rules of the cellular automata currently
|
||||
@ -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
|
||||
25
docs/src/highlevel-design/fluid/fluidbifrication.md
Normal file
25
docs/src/highlevel-design/fluid/fluidbifrication.md
Normal file
@ -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.
|
||||
|
||||
11
docs/src/highlevel-design/fluid/fluidcellularautomata.md
Normal file
11
docs/src/highlevel-design/fluid/fluidcellularautomata.md
Normal file
@ -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
|
||||
17
docs/src/highlevel-design/fluid/fluidchunk.md
Normal file
17
docs/src/highlevel-design/fluid/fluidchunk.md
Normal file
@ -0,0 +1,17 @@
|
||||
@page fluidchunk Fluid Chunk
|
||||
|
||||
# What data it has
|
||||
|
||||
Density - The amount of density at each point in the chunk
|
||||
|
||||
Vector <u,v,w> - 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
|
||||
|
||||
6
docs/src/highlevel-design/fluid/fluidindex.md
Normal file
6
docs/src/highlevel-design/fluid/fluidindex.md
Normal file
@ -0,0 +1,6 @@
|
||||
@page fluidindex Fluid Simulation
|
||||
|
||||
[TOC]
|
||||
- @subpage fluidbifrication
|
||||
- @subpage fluidchunk
|
||||
- @subpage fluidcellularautomata
|
||||
@ -13,3 +13,5 @@ Discussion of, at a high game-design level, how everything should work and conne
|
||||
- @subpage narrativemanager
|
||||
- @subpage itemsindex
|
||||
- @subpage puzzleindex
|
||||
- @subpage fluidindex
|
||||
- @subpage locomotion
|
||||
@ -41,6 +41,8 @@
|
||||
|
||||
## Ruins
|
||||
|
||||
## Light Valley
|
||||
Valley with lights shooting around the sky from projectors placed sporadically around the valley, tie into lore and provide some kind of function (portal controller or something?)
|
||||
|
||||
|
||||
|
||||
@ -287,3 +289,20 @@ Plains with lots of fire
|
||||
## Thunder Planes
|
||||
Constant Thunderstorms and lots of lightning strikes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Sky Specific
|
||||
|
||||
## Sky Reefs
|
||||
Full of kelp dangling from island, lots of sea-themed flying creatures
|
||||
|
||||
## Ghost Water in the Sky
|
||||
Idk what this means but I just came up with it and it sounds cool
|
||||
|
||||
|
||||
@ -3,4 +3,10 @@
|
||||
|
||||
### Natural Colosseum
|
||||
|
||||
###
|
||||
### "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.
|
||||
|
||||
### Solitairy Giant
|
||||
Enclosed zone centered around a silent giant performing some toil (walking in circles around the periphery? Moving boulders? idk)
|
||||
Ambient audio effect of him doing work the whole time
|
||||
|
||||
|
||||
22
docs/src/highlevel-design/locomotion/locomotion.md
Normal file
22
docs/src/highlevel-design/locomotion/locomotion.md
Normal file
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -5,3 +5,4 @@ Everything puzzles
|
||||
[TOC]
|
||||
- @subpage puzzlehighlevelflow
|
||||
- @subpage puzzleideas
|
||||
- @subpage puzzlespells
|
||||
@ -3,7 +3,7 @@
|
||||
```
|
||||
generate 200 x 200
|
||||
interpolate x 20 in each direction
|
||||
this map will be 0.25 km resolution
|
||||
this map will be 0.25 km resolution and ~62mb in size
|
||||
Useful for macro sim
|
||||
```
|
||||
|
||||
|
||||
19
docs/src/progress/bigthings.md
Normal file
19
docs/src/progress/bigthings.md
Normal file
@ -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)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@page renderertodo TODO
|
||||
|
||||
|
||||
@subpage bigthings Big Things To Build
|
||||
|
||||
|
||||
|
||||
@ -153,36 +153,51 @@ 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 :| )
|
||||
(03/20/2024)
|
||||
Free camera system that can detatch from player entity
|
||||
|
||||
(03/20/2024)
|
||||
Half pass at cellular automata fluid dynamics system
|
||||
- Diffuse density
|
||||
- Streaming chunks over network
|
||||
- Basic model creation
|
||||
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
Fix character movement
|
||||
- Walking left or right while turning camera is very jittery
|
||||
- Can lock on moving
|
||||
|
||||
Fix Frustum Culling for skybox
|
||||
|
||||
Fix Character creation preview not working
|
||||
|
||||
|
||||
Clean up main method/class
|
||||
|
||||
Include Remotery library
|
||||
|
||||
Physics-controlled objects system
|
||||
|
||||
Shader library system
|
||||
- Abiltiy to include the shader library in individual files (ie implement #include)
|
||||
|
||||
Level loading/saving + Basic Editor
|
||||
- Spin up voxel level (think arena mode)
|
||||
- Save voxel level
|
||||
- Basic editor functionality
|
||||
- Menu of types of entities to spawn
|
||||
- Button to spawn them at cursor
|
||||
|
||||
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)
|
||||
@ -199,10 +214,30 @@ Transvoxel Algorithm
|
||||
- Prebake all textures into atlas
|
||||
- Rewrite marching cubes shader to leverage this atlas
|
||||
|
||||
Another pass at grass
|
||||
- Fix shader being camera position independent (if you move the wind moves with you lol)
|
||||
- Multiple foliage models in same cell
|
||||
|
||||
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 ???
|
||||
|
||||
|
||||
Cellular Automata Fluid Dynamics System
|
||||
- Advect force
|
||||
- Advect density
|
||||
- Diffuse density
|
||||
- Do not bound to single chunks
|
||||
- Only add compression when it starts to become an issue
|
||||
|
||||
|
||||
skybox work
|
||||
@ -258,6 +293,8 @@ Upgrade terrain editing user experience further
|
||||
- Lock to axis tools
|
||||
- Server validation for client request to change terrain
|
||||
|
||||
Bring LWJGL version up to latest
|
||||
|
||||
|
||||
|
||||
|
||||
@ -277,12 +314,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 +327,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
|
||||
|
||||
|
||||
|
||||
@ -321,3 +359,12 @@ dynamic camp/house system - npcs will gradually join your camp the longer you st
|
||||
dynamic warfare system
|
||||
- Guard towers that need to be captured by factions before enabling assault on real settlements
|
||||
- Raids against villages
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Known bugs
|
||||
- Draw cell manager iso values dont make sense and should scale empty cells based on neighbor cells
|
||||
- Draw cell manager logic doesn't fill in border cells properly (the logic to check if a border cell exists always succeeds as long as the potential location is within world bounds, not if it actually exists in cache)
|
||||
- Control handler re-polls for mouse coordiantes for each control handler group it processes, so only the first group gets the mouse movement event properly
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
|
||||
[TOC]
|
||||
- @subpage actorsindex
|
||||
- @subpage DrawCell
|
||||
- @subpage Fonts
|
||||
- @subpage modelLoading
|
||||
- @subpage instanceindex
|
||||
@ -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
|
||||
@ -184,6 +184,32 @@
|
||||
"worldZ",
|
||||
"chunkData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "RequestFluidData",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "sendFluidData",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"chunkData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "updateFluidData",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"chunkData"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.Scene;
|
||||
import electrosphere.entity.types.attach.AttachUtils;
|
||||
|
||||
@Deprecated
|
||||
public class ClientEntityCullingManager {
|
||||
|
||||
Scene scene;
|
||||
@ -40,38 +41,4 @@ public class ClientEntityCullingManager {
|
||||
target.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
}
|
||||
|
||||
public void clearOutOfBoundsEntities(){
|
||||
if(Globals.commonWorldData != null && Globals.playerEntity != null && Globals.clientPlayerData != null){
|
||||
Vector3d playerCharacterPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
int playerCharacterWorldX = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.x);
|
||||
int playerCharacterWorldY = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.y);
|
||||
int playerCharacterWorldZ = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.z);
|
||||
if(
|
||||
playerCharacterWorldX != Globals.clientPlayerData.getWorldPos().x||
|
||||
playerCharacterWorldY != Globals.clientPlayerData.getWorldPos().y ||
|
||||
playerCharacterWorldZ != Globals.clientPlayerData.getWorldPos().z
|
||||
){
|
||||
for(Entity entity : scene.getEntityList()){
|
||||
if(entity.containsKey(EntityDataStrings.TERRAIN_IS_TERRAIN) || entity.containsKey(EntityDataStrings.ATTACH_PARENT) || entity.containsKey(EntityDataStrings.COLLISION_ENTITY_PARENT)){
|
||||
|
||||
} else {
|
||||
Vector3d position = EntityUtils.getPosition(entity);
|
||||
//common world data is initialized with the collision data
|
||||
//if this is null then the engine hasn't fully started up yet
|
||||
if(position != null){
|
||||
int worldX = Globals.clientWorldData.convertRealToWorld(position.x);
|
||||
int worldZ = Globals.clientWorldData.convertRealToWorld(position.z);
|
||||
if(!Globals.drawCellManager.coordsInPhysicsSpace(worldX, worldZ)){
|
||||
//we need to just hide the entity
|
||||
recursiveHide(entity);
|
||||
} else {
|
||||
//if the entity is within range and it's not set to visible, make it visible
|
||||
recursiveShow(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,48 +17,15 @@ public class FluidChunkData {
|
||||
//The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk
|
||||
public static final int CHUNK_DATA_GENERATOR_SIZE = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE;
|
||||
|
||||
//What type of fluid is in this voxel, eg stone vs dirt vs grass, etc
|
||||
int[][][] voxelType;
|
||||
//How much of that fluid type is in this voxel
|
||||
float[][][] voxelWeight;
|
||||
|
||||
//the list of positions modified since the last call to resetModifiedPositions
|
||||
//Used in DrawCell to keep track of which positions to invalidate
|
||||
Set<String> modifiedSinceLastGeneration = new HashSet<String>();
|
||||
//The velocities
|
||||
float[][][] velocityX;
|
||||
float[][][] velocityY;
|
||||
float[][][] velocityZ;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the voxel type array in this container
|
||||
* @return The voxel type array
|
||||
*/
|
||||
public int[][][] getVoxelType(){
|
||||
return voxelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the voxel type array in this container
|
||||
* @param voxelType The voxel type array
|
||||
*/
|
||||
public void setVoxelType(int[][][] voxelType){
|
||||
//mark changed cells
|
||||
if(this.voxelType != null){
|
||||
for(int x = 0; x < CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < CHUNK_SIZE; z++){
|
||||
if(voxelType[x][y][z] != this.voxelType[x][y][z]){
|
||||
String key = getVoxelPositionKey(new Vector3i(x,y,z));
|
||||
if(!modifiedSinceLastGeneration.contains(key)){
|
||||
modifiedSinceLastGeneration.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//update data
|
||||
this.voxelType = voxelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the voxel weight array in this container
|
||||
* @return The voxel weight array
|
||||
@ -72,43 +39,10 @@ public class FluidChunkData {
|
||||
* @param voxelWeight The voxel weight array
|
||||
*/
|
||||
public void setVoxelWeight(float[][][] voxelWeight){
|
||||
//mark changed cells
|
||||
if(this.voxelWeight != null){
|
||||
for(int x = 0; x < CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < CHUNK_SIZE; z++){
|
||||
if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){
|
||||
String key = getVoxelPositionKey(new Vector3i(x,y,z));
|
||||
if(!modifiedSinceLastGeneration.contains(key)){
|
||||
modifiedSinceLastGeneration.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//update data
|
||||
this.voxelWeight = voxelWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value of a single voxel in the chunk
|
||||
* @param localX The local position X
|
||||
* @param localY The local position Y
|
||||
* @param localZ The local position Z
|
||||
* @param weight The weight to set it to
|
||||
* @param type The type to set the voxel to
|
||||
*/
|
||||
public void updatePosition(int localX, int localY, int localZ, float weight, int type){
|
||||
voxelWeight[localX][localY][localZ] = weight;
|
||||
voxelType[localX][localY][localZ] = type;
|
||||
//store as modified in cache
|
||||
String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ));
|
||||
if(!modifiedSinceLastGeneration.contains(key)){
|
||||
modifiedSinceLastGeneration.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight of a voxel at a poisiton
|
||||
* @param localPosition The local position
|
||||
@ -119,32 +53,11 @@ public class FluidChunkData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of a voxel at a position
|
||||
* @param localPosition The local position
|
||||
* @return The type of the specified voxel
|
||||
* Gets the weight of a voxel at a poisiton
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public int getType(Vector3i localPosition){
|
||||
return voxelType[localPosition.x][localPosition.y][localPosition.z];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache of modified positions
|
||||
*/
|
||||
public void resetModifiedPositions(){
|
||||
this.modifiedSinceLastGeneration.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of all modified positions since the last call to resetModifiedPositions
|
||||
* @return The set of all modified positions
|
||||
*/
|
||||
public Set<Vector3i> getModifiedPositions(){
|
||||
Set<Vector3i> rVal = new HashSet<Vector3i>();
|
||||
for(String key : modifiedSinceLastGeneration){
|
||||
String[] split = key.split("_");
|
||||
rVal.add(new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2])));
|
||||
}
|
||||
return rVal;
|
||||
public float getWeight(int x, int y, int z){
|
||||
return voxelWeight[x][y][z];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
296
src/main/java/electrosphere/client/fluid/cells/FluidCell.java
Normal file
296
src/main/java/electrosphere/client/fluid/cells/FluidCell.java
Normal file
@ -0,0 +1,296 @@
|
||||
package electrosphere.client.fluid.cells;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
|
||||
import electrosphere.client.fluid.cache.FluidChunkData;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.fluid.FluidChunk;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author satellite
|
||||
*/
|
||||
public class FluidCell {
|
||||
//the position of the draw cell in world coordinates
|
||||
Vector3i worldPos;
|
||||
|
||||
FluidChunkData data;
|
||||
|
||||
Entity modelEntity;
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
DBody physicsObject;
|
||||
|
||||
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
|
||||
|
||||
//the value of an empty fluid cell weight that is not neighbored by a fluid value
|
||||
public static final float ISO_SURFACE_EMPTY = -1;
|
||||
|
||||
FluidCell(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a drawcell object
|
||||
*/
|
||||
public static FluidCell generateFluidCell(
|
||||
Vector3i worldPos,
|
||||
FluidChunkData data,
|
||||
ShaderProgram program
|
||||
){
|
||||
FluidCell rVal = new FluidCell();
|
||||
rVal.worldPos = worldPos;
|
||||
rVal.program = program;
|
||||
rVal.data = data;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a drawable entity based on this chunk
|
||||
*/
|
||||
public void generateDrawableEntity(){
|
||||
if(modelEntity != null){
|
||||
Globals.clientScene.deregisterEntity(modelEntity);
|
||||
}
|
||||
|
||||
fillInData();
|
||||
|
||||
modelEntity = FluidChunk.clientCreateFluidChunkEntity(weights);
|
||||
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
|
||||
}
|
||||
|
||||
protected Vector3d getRealPos(){
|
||||
return new Vector3d(
|
||||
worldPos.x * FluidChunkData.CHUNK_SIZE,
|
||||
worldPos.y * FluidChunkData.CHUNK_SIZE,
|
||||
worldPos.z * FluidChunkData.CHUNK_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a drawcell including its physics
|
||||
*/
|
||||
public void destroy(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
collisionEngine.destroyEntityThatHasPhysics(modelEntity);
|
||||
EntityUtils.cleanUpEntity(modelEntity);
|
||||
//destruct model
|
||||
String modelPath = (String)modelEntity.getData(EntityDataStrings.DATA_STRING_MODEL_PATH);
|
||||
Globals.assetManager.deregisterModelPath(modelPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current chunk data for this draw cell
|
||||
* @return The chunk data
|
||||
*/
|
||||
public FluidChunkData getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in the internal arrays of data for generate terrain models
|
||||
*/
|
||||
private void fillInData(){
|
||||
|
||||
//
|
||||
//fill in data
|
||||
//
|
||||
//main chunk
|
||||
FluidChunkData currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos);
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
weights[x][y][z] = currentChunk.getWeight(x,y,z);
|
||||
}
|
||||
}
|
||||
}
|
||||
//face X
|
||||
if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = currentChunk.getWeight(0, i, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
//face Y
|
||||
if(worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = currentChunk.getWeight(i, 0, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
//face Z
|
||||
if(worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z + 1);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, j, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
//edge X-Y
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = currentChunk.getWeight(0, 0, i);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
//edge X-Z
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z + 1);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, i, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
//edge Y-Z
|
||||
if(
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z + 1);
|
||||
if(currentChunk != null){
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, 0, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
|
||||
if(currentChunk != null){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, 0, 0);
|
||||
} else {
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
} else {
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY;
|
||||
}
|
||||
|
||||
//now set neighboring air weights based on nearby fluid count
|
||||
//idea being that we dont have the snapping behavior from iso surface jumping from -1->0.01
|
||||
int[] neighborIndexX = new int[]{-1,1,0,0,0,0};
|
||||
int[] neighborIndexY = new int[]{0,0,-1,1,0,0};
|
||||
int[] neighborIndexZ = new int[]{0,0,0,0,-1,1};
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||
if(weights[x][y][z] > 0){
|
||||
continue;
|
||||
}
|
||||
for(int i = 0; i < 6; i++){
|
||||
int currX = x + neighborIndexX[i];
|
||||
int currY = y + neighborIndexY[i];
|
||||
int currZ = z + neighborIndexZ[i];
|
||||
if(
|
||||
currX >= 0 && currX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE &&
|
||||
currY >= 0 && currY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE &&
|
||||
currZ >= 0 && currZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE &&
|
||||
(1 + weights[x][y][z]) < weights[currX][currY][currZ]
|
||||
){
|
||||
weights[x][y][z] = -(1 - weights[currX][currY][currZ]);
|
||||
if(weights[x][y][z] >= 0){
|
||||
weights[x][y][z] = -0.01f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,7 +19,7 @@ import electrosphere.renderer.shader.ShaderProgram;
|
||||
*
|
||||
* @author satellite
|
||||
*/
|
||||
public class FluidDrawCellManager {
|
||||
public class FluidCellManager {
|
||||
|
||||
|
||||
//the center of this cell manager's array in cell space
|
||||
@ -35,8 +35,8 @@ public class FluidDrawCellManager {
|
||||
int miniCellWidth;
|
||||
|
||||
//all currently displaying mini cells
|
||||
Set<FluidDrawCell> cells;
|
||||
Map<String,FluidDrawCell> keyCellMap = new HashMap<String,FluidDrawCell>();
|
||||
Set<FluidCell> cells;
|
||||
Map<String,FluidCell> keyCellMap = new HashMap<String,FluidCell>();
|
||||
Set<String> hasNotRequested;
|
||||
Set<String> hasRequested;
|
||||
Set<String> drawable;
|
||||
@ -59,8 +59,6 @@ public class FluidDrawCellManager {
|
||||
int worldBoundDiscreteMin = 0;
|
||||
int worldBoundDiscreteMax = 0;
|
||||
|
||||
//client terrain manager
|
||||
// ClientTerrainManager clientTerrainManager;
|
||||
|
||||
|
||||
//ready to start updating?
|
||||
@ -78,9 +76,9 @@ public class FluidDrawCellManager {
|
||||
* @param discreteX The initial discrete position X coordinate
|
||||
* @param discreteY The initial discrete position Y coordinate
|
||||
*/
|
||||
public FluidDrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
|
||||
public FluidCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
|
||||
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f);
|
||||
cells = new HashSet<FluidDrawCell>();
|
||||
cells = new HashSet<FluidCell>();
|
||||
hasNotRequested = new HashSet<String>();
|
||||
drawable = new HashSet<String>();
|
||||
undrawable = new HashSet<String>();
|
||||
@ -102,7 +100,7 @@ public class FluidDrawCellManager {
|
||||
update = true;
|
||||
}
|
||||
|
||||
FluidDrawCellManager(){
|
||||
FluidCellManager(){
|
||||
|
||||
}
|
||||
|
||||
@ -132,7 +130,7 @@ public class FluidDrawCellManager {
|
||||
){
|
||||
// if(!hasRequested.contains(targetKey)){
|
||||
//client should request chunk data from server
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestFluidDataMessage(
|
||||
worldPos.x,
|
||||
worldPos.y,
|
||||
worldPos.z
|
||||
@ -161,7 +159,7 @@ public class FluidDrawCellManager {
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
if(containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){
|
||||
FluidDrawCell cell = FluidDrawCell.generateFluidCell(
|
||||
FluidCell cell = FluidCell.generateFluidCell(
|
||||
worldPos,
|
||||
Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z),
|
||||
program
|
||||
@ -256,14 +254,14 @@ public class FluidDrawCellManager {
|
||||
* Clears all cells outside of draw radius
|
||||
*/
|
||||
private void clearOutOfBoundsCells(){
|
||||
Set<FluidDrawCell> cellsToRemove = new HashSet<FluidDrawCell>();
|
||||
for(FluidDrawCell cell : cells){
|
||||
Set<FluidCell> cellsToRemove = new HashSet<FluidCell>();
|
||||
for(FluidCell cell : cells){
|
||||
Vector3d realPos = cell.getRealPos();
|
||||
if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){
|
||||
cellsToRemove.add(cell);
|
||||
}
|
||||
}
|
||||
for(FluidDrawCell cell : cellsToRemove){
|
||||
for(FluidCell cell : cellsToRemove){
|
||||
cells.remove(cell);
|
||||
String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z);
|
||||
hasNotRequested.remove(key);
|
||||
@ -358,8 +356,8 @@ public class FluidDrawCellManager {
|
||||
}
|
||||
|
||||
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
if(Globals.clientFluidManager != null){
|
||||
return Globals.clientFluidManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
package electrosphere.client.fluid.cells;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
|
||||
import electrosphere.client.fluid.cache.FluidChunkData;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.fluid.FluidChunk;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author satellite
|
||||
*/
|
||||
public class FluidDrawCell {
|
||||
//the position of the draw cell in world coordinates
|
||||
Vector3i worldPos;
|
||||
|
||||
FluidChunkData data;
|
||||
|
||||
Entity modelEntity;
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
DBody physicsObject;
|
||||
|
||||
static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png");
|
||||
static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png");
|
||||
static Texture groundTextureThree = new Texture("/Textures/Ground/Dirt1.png");
|
||||
static Texture groundTextureFour = new Texture("/Textures/Ground/Dirt1.png");
|
||||
|
||||
static {
|
||||
// groundTextureOne = new Texture("/Textures/Ground/GrassTileable.png");
|
||||
// groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png");
|
||||
// groundTextureThree = new Texture("/Textures/Ground/Dirt1.png");
|
||||
// groundTextureFour = new Texture("/Textures/Ground/Dirt1.png");
|
||||
}
|
||||
|
||||
|
||||
FluidDrawCell(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a drawcell object
|
||||
*/
|
||||
public static FluidDrawCell generateFluidCell(
|
||||
Vector3i worldPos,
|
||||
FluidChunkData data,
|
||||
ShaderProgram program
|
||||
){
|
||||
FluidDrawCell rVal = new FluidDrawCell();
|
||||
rVal.worldPos = worldPos;
|
||||
rVal.program = program;
|
||||
rVal.data = data;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a drawable entity based on this chunk
|
||||
*/
|
||||
public void generateDrawableEntity(){
|
||||
if(modelEntity != null){
|
||||
Globals.clientScene.deregisterEntity(modelEntity);
|
||||
}
|
||||
modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight(), data.getVoxelType());
|
||||
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
|
||||
}
|
||||
|
||||
protected Vector3d getRealPos(){
|
||||
return new Vector3d(
|
||||
worldPos.x * FluidChunkData.CHUNK_SIZE,
|
||||
worldPos.y * FluidChunkData.CHUNK_SIZE,
|
||||
worldPos.z * FluidChunkData.CHUNK_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a drawcell including its physics
|
||||
*/
|
||||
public void destroy(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
collisionEngine.destroyEntityThatHasPhysics(modelEntity);
|
||||
EntityUtils.cleanUpEntity(modelEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current chunk data for this draw cell
|
||||
* @return The chunk data
|
||||
*/
|
||||
public FluidChunkData getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,6 +12,7 @@ import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.fluid.cache.ClientFluidCache;
|
||||
import electrosphere.client.fluid.cache.FluidChunkData;
|
||||
import electrosphere.client.fluid.cells.FluidCell;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.fluid.FluidChunkModelData;
|
||||
@ -57,35 +58,25 @@ public class ClientFluidManager {
|
||||
for(TerrainMessage message : messageQueue){
|
||||
messageQueue.remove(message);
|
||||
switch(message.getMessageSubtype()){
|
||||
case SENDCHUNKDATA: {
|
||||
int[][][] values = new int[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE];
|
||||
float[][][] weights = new float[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE];
|
||||
case SENDFLUIDDATA: {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
|
||||
FloatBuffer floatBuffer = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||
weights[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
IntBuffer intView = buffer.asIntBuffer();
|
||||
intView.position(floatBuffer.position());
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||
values[x][y][z] = intView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
FluidChunkData data = new FluidChunkData();
|
||||
data.setVoxelType(values);
|
||||
data.setVoxelWeight(weights);
|
||||
FluidChunkData data = parseFluidDataBuffer(buffer);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
} break;
|
||||
case UPDATEFLUIDDATA: {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
|
||||
FluidChunkData data = parseFluidDataBuffer(buffer);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
if(Globals.fluidCellManager != null){
|
||||
Globals.fluidCellManager.markUpdateable(message.getworldX(), message.getworldY(), message.getworldZ());
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerEngine.WARNING("ClientFluidManager: unhandled network message of type" + message.getMessageSubtype());
|
||||
break;
|
||||
@ -96,7 +87,7 @@ public class ClientFluidManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void attachTerrainMessage(TerrainMessage message){
|
||||
public void attachFluidMessage(TerrainMessage message){
|
||||
messageQueue.add(message);
|
||||
}
|
||||
|
||||
@ -160,4 +151,51 @@ public class ClientFluidManager {
|
||||
fluidChunkGenerationQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a byte buffer into a fluid data object
|
||||
* @param buffer the buffer
|
||||
* @return the object
|
||||
*/
|
||||
private FluidChunkData parseFluidDataBuffer(ByteBuffer buffer){
|
||||
float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
FloatBuffer floatBuffer = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
weights[x][y][z] = floatBuffer.get();
|
||||
if(weights[x][y][z] <= 0){
|
||||
weights[x][y][z] = FluidCell.ISO_SURFACE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityX[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityY[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityZ[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
FluidChunkData data = new FluidChunkData();
|
||||
data.setVoxelWeight(weights);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<String,FoliageCell> locationCellMap = new HashMap<String,FoliageCell>();
|
||||
//The maximum distance a cell can be away from the player before being destroyed
|
||||
static final float CELL_DISTANCE_MAX = 25f;
|
||||
static final float CELL_DISTANCE_MAX = 5f;
|
||||
|
||||
//The maximum number of foliage cells
|
||||
static final int CELL_COUNT_MAX = 25;
|
||||
|
||||
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<String,Integer> locationEvaluationCooldownMap = new ConcurrentHashMap<String,Integer>();
|
||||
@ -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,6 +299,12 @@ 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
|
||||
@ -306,39 +320,145 @@ public class ClientFoliageManager {
|
||||
worldPos.y * ChunkData.CHUNK_SIZE + voxelPos.y,
|
||||
worldPos.z * ChunkData.CHUNK_SIZE + voxelPos.z
|
||||
);
|
||||
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);
|
||||
|
||||
//create cell and buffer
|
||||
FoliageCell cell = new FoliageCell(worldPos, voxelPos, realPos);
|
||||
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);
|
||||
//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 offsetX = placementRandomizer.nextDouble() - 0.5;
|
||||
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;
|
||||
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){
|
||||
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();
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the voxel type supports foliage or not
|
||||
@ -387,6 +507,15 @@ public class ClientFoliageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all foliage in the foliage manager
|
||||
*/
|
||||
public void draw(){
|
||||
for(FoliageCell cell : activeCells){
|
||||
cell.draw(modelMatrixAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.client.sim;
|
||||
|
||||
import electrosphere.client.fluid.manager.ClientFluidManager;
|
||||
import electrosphere.client.instancing.InstanceUpdater;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
@ -27,9 +28,11 @@ public class ClientFunctions {
|
||||
|
||||
public static void runClientFunctions(){
|
||||
ClientTerrainManager.generateTerrainChunkGeometry();
|
||||
ClientFluidManager.generateFluidChunkGeometry();
|
||||
updateSkyboxPos();
|
||||
Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange();
|
||||
InstanceUpdater.updateInstancedActorPriority();
|
||||
Globals.cameraHandler.updateGlobalCamera();
|
||||
// updateCellManager();
|
||||
}
|
||||
|
||||
@ -48,11 +51,15 @@ public class ClientFunctions {
|
||||
public static void loadTerrain(){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
Globals.clientTerrainManager.handleMessages();
|
||||
updateCellManager();
|
||||
updateTerrainCellManager();
|
||||
}
|
||||
if(Globals.clientFluidManager != null){
|
||||
Globals.clientFluidManager.handleMessages();
|
||||
updateFluidCellManager();
|
||||
}
|
||||
}
|
||||
|
||||
static void updateCellManager(){
|
||||
static void updateTerrainCellManager(){
|
||||
///
|
||||
/// C L I E N T C E L L M A N A G E R
|
||||
///
|
||||
@ -65,4 +72,15 @@ public class ClientFunctions {
|
||||
Globals.drawCellManager.update();
|
||||
}
|
||||
}
|
||||
|
||||
static void updateFluidCellManager(){
|
||||
//fluid work
|
||||
if(Globals.fluidCellManager != null && Globals.clientWorldData != null){
|
||||
if(Globals.playerEntity != null){
|
||||
newPlayerCharacterPosition = EntityUtils.getPosition(Globals.playerEntity);
|
||||
}
|
||||
Globals.fluidCellManager.calculateDeltas(newPlayerCharacterPosition);
|
||||
Globals.fluidCellManager.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ public class CameraHandler {
|
||||
float pitch = 50;
|
||||
Vector3f cameraRotationVector = new Vector3f();
|
||||
Vector3f radialOffset = new Vector3f(0,1,0);
|
||||
boolean trackPlayerEntity = true;
|
||||
|
||||
public void handleMouseEvent(MouseEvent event){
|
||||
|
||||
@ -89,6 +90,10 @@ public class CameraHandler {
|
||||
// cameraRotationVector.normalize();
|
||||
// System.out.println(yaw + " " + pitch);
|
||||
}
|
||||
if(trackPlayerEntity && Globals.playerEntity != null){
|
||||
Vector3d entityPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
CameraEntityUtils.setCameraCenter(Globals.playerCamera, new Vector3f((float)entityPos.x,(float)entityPos.y,(float)entityPos.z).add(CameraEntityUtils.getOrbitalCameraRadialOffset(Globals.playerCamera)));
|
||||
}
|
||||
//update view matrix offset
|
||||
float xFactor = (float)Math.cos(yaw / 180.0f * Math.PI);
|
||||
float yFactor = (float)Math.sin(yaw / 180.0f * Math.PI);
|
||||
@ -110,4 +115,17 @@ public class CameraHandler {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
//set player tracking
|
||||
public void setTrackPlayerEntity(boolean track){
|
||||
trackPlayerEntity = track;
|
||||
}
|
||||
|
||||
//get trackPlayerEntity
|
||||
public boolean getTrackPlayerEntity(){
|
||||
return trackPlayerEntity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -195,11 +195,21 @@ public class ControlHandler {
|
||||
public static final String DEBUG_OPEN_DEBUG_MENU = "openDebugMenu";
|
||||
|
||||
|
||||
public static final String FREECAM_UP = "freecamUp";
|
||||
public static final String FREECAM_DOWN = "freecamDown";
|
||||
public static final String FREECAM_FORWARD = "freecamForward";
|
||||
public static final String FREECAM_BACKWARD = "freecamBackward";
|
||||
public static final String FREECAM_LEFT = "freecamLeft";
|
||||
public static final String FREECAM_RIGHT = "freecamRight";
|
||||
public static final String FREECAM_MOUSE = "freecamMouse";
|
||||
|
||||
|
||||
public static enum ControlsState {
|
||||
TITLE_PAGE,
|
||||
TITLE_MENU,
|
||||
MAIN_GAME,
|
||||
IN_GAME_MAIN_MENU,
|
||||
IN_GAME_FREE_CAMERA,
|
||||
INVENTORY,
|
||||
NO_INPUT,
|
||||
}
|
||||
@ -232,6 +242,7 @@ public class ControlHandler {
|
||||
List<Control> typingControlList = new LinkedList<Control>();
|
||||
List<Control> inventoryControlList = new LinkedList<Control>();
|
||||
List<Control> alwaysOnDebugControlList = new LinkedList<Control>();
|
||||
List<Control> freeCameraControlList = new LinkedList<Control>();
|
||||
|
||||
ControlHandler(){
|
||||
controls = new HashMap<String, Control>();
|
||||
@ -342,6 +353,18 @@ public class ControlHandler {
|
||||
*/
|
||||
handler.setHandlerState(ControlsState.TITLE_MENU);
|
||||
|
||||
/*
|
||||
* Free camera
|
||||
*/
|
||||
handler.addControl(FREECAM_UP, new Control(ControlType.KEY,GLFW_KEY_SPACE));
|
||||
handler.addControl(FREECAM_DOWN, new Control(ControlType.KEY,GLFW_KEY_LEFT_CONTROL));
|
||||
handler.addControl(FREECAM_FORWARD, new Control(ControlType.KEY,GLFW_KEY_W));
|
||||
handler.addControl(FREECAM_BACKWARD, new Control(ControlType.KEY,GLFW_KEY_S));
|
||||
handler.addControl(FREECAM_LEFT, new Control(ControlType.KEY,GLFW_KEY_A));
|
||||
handler.addControl(FREECAM_RIGHT, new Control(ControlType.KEY,GLFW_KEY_D));
|
||||
handler.addControl(FREECAM_MOUSE, new Control(ControlType.MOUSE_MOVEMENT,0));
|
||||
|
||||
|
||||
/*
|
||||
Save to file
|
||||
*/
|
||||
@ -395,6 +418,12 @@ public class ControlHandler {
|
||||
// pollMenuNavigationControls();
|
||||
break;
|
||||
|
||||
case IN_GAME_FREE_CAMERA:
|
||||
runHandlers(freeCameraControlList);
|
||||
runHandlers(mainGameDebugControlList);
|
||||
runHandlers(alwaysOnDebugControlList);
|
||||
break;
|
||||
|
||||
case INVENTORY:
|
||||
runHandlers(inventoryControlList);
|
||||
runHandlers(menuNavigationControlList);
|
||||
@ -416,6 +445,7 @@ public class ControlHandler {
|
||||
setTypingControls();
|
||||
setInventoryControls();
|
||||
setAlwaysOnDebugControls();
|
||||
setFreecamControls();
|
||||
}
|
||||
|
||||
void setMainGameControls(){
|
||||
@ -1122,6 +1152,48 @@ public class ControlHandler {
|
||||
|
||||
}
|
||||
|
||||
void setFreecamControls(){
|
||||
freeCameraControlList.add(controls.get(FREECAM_UP));
|
||||
controls.get(FREECAM_UP).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
playerCameraCenterPos.add(0,0.1f,0);
|
||||
CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
|
||||
}});
|
||||
|
||||
|
||||
freeCameraControlList.add(controls.get(FREECAM_DOWN));
|
||||
controls.get(FREECAM_DOWN).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
playerCameraCenterPos.add(0,-0.1f,0);
|
||||
CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
|
||||
}});
|
||||
|
||||
|
||||
freeCameraControlList.add(controls.get(FREECAM_FORWARD));
|
||||
controls.get(FREECAM_FORWARD).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
|
||||
playerCameraCenterPos.add(new Vector3f(playerCameraEyePos).mul(-0.1f));
|
||||
CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
|
||||
}});
|
||||
|
||||
freeCameraControlList.add(controls.get(FREECAM_BACKWARD));
|
||||
controls.get(FREECAM_BACKWARD).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
|
||||
playerCameraCenterPos.add(new Vector3f(playerCameraEyePos).mul(0.1f));
|
||||
CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
|
||||
}});
|
||||
|
||||
freeCameraControlList.add(controls.get(FREECAM_MOUSE));
|
||||
controls.get(FREECAM_MOUSE).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent event){
|
||||
Globals.cameraHandler.handleMouseEvent(event);
|
||||
}});
|
||||
|
||||
freeCameraControlList.add(controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU));
|
||||
|
||||
}
|
||||
|
||||
void setTypingControls(){
|
||||
|
||||
String[] typeKeybinds = {
|
||||
@ -1353,6 +1425,10 @@ public class ControlHandler {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ControlsState getHandlerState(){
|
||||
return state;
|
||||
}
|
||||
|
||||
public ControlsState getState(){
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import electrosphere.audio.AudioEngine;
|
||||
import electrosphere.audio.VirtualAudioSourceManager;
|
||||
import electrosphere.auth.AuthenticationManager;
|
||||
import electrosphere.client.culling.ClientEntityCullingManager;
|
||||
import electrosphere.client.fluid.cells.FluidCellManager;
|
||||
import electrosphere.client.fluid.manager.ClientFluidManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
import electrosphere.client.player.ClientPlayerData;
|
||||
@ -52,6 +53,7 @@ import electrosphere.renderer.actor.instance.InstanceManager;
|
||||
import electrosphere.renderer.light.PointLight;
|
||||
import electrosphere.renderer.light.SpotLight;
|
||||
import electrosphere.renderer.loading.ModelPretransforms;
|
||||
import electrosphere.renderer.meshgen.FluidChunkModelGeneration;
|
||||
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
|
||||
import electrosphere.renderer.model.Material;
|
||||
import electrosphere.renderer.shader.ShaderOptionMap;
|
||||
@ -66,6 +68,7 @@ import electrosphere.server.content.ServerContentManager;
|
||||
import electrosphere.server.datacell.EntityDataCellMapper;
|
||||
import electrosphere.server.datacell.RealmManager;
|
||||
import electrosphere.server.db.DatabaseController;
|
||||
import electrosphere.server.fluid.manager.ServerFluidManager;
|
||||
import electrosphere.server.pathfinding.NavMeshManager;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
import electrosphere.server.simulation.MicroSimulation;
|
||||
@ -254,6 +257,12 @@ public class Globals {
|
||||
//terrain manager
|
||||
// public static boolean LOAD_TERRAIN = true;
|
||||
public static ServerTerrainManager serverTerrainManager;
|
||||
|
||||
|
||||
//fluid manager
|
||||
public static ServerFluidManager serverFluidManager;
|
||||
|
||||
//spawn point
|
||||
public static Vector3d spawnPoint = new Vector3d(0,0,0);
|
||||
|
||||
//content manager
|
||||
@ -304,9 +313,12 @@ public class Globals {
|
||||
public static ClientPlayerData clientPlayerData = new ClientPlayerData();
|
||||
|
||||
//chunk stuff
|
||||
//constant for how far in game units you have to move to load chunks
|
||||
//draw cell manager
|
||||
public static DrawCellManager drawCellManager;
|
||||
|
||||
//fluid cell manager
|
||||
public static FluidCellManager fluidCellManager;
|
||||
|
||||
//navmesh manager
|
||||
public static NavMeshManager navMeshManager;
|
||||
|
||||
@ -413,6 +425,8 @@ public class Globals {
|
||||
navMeshManager = new NavMeshManager();
|
||||
//terrain
|
||||
Globals.clientTerrainManager = new ClientTerrainManager();
|
||||
//fluid
|
||||
Globals.clientFluidManager = new ClientFluidManager();
|
||||
//game config
|
||||
gameConfigDefault = electrosphere.game.data.Config.loadDefaultConfig();
|
||||
gameConfigCurrent = gameConfigDefault;
|
||||
@ -471,8 +485,7 @@ public class Globals {
|
||||
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
|
||||
TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
|
||||
//init fluid shader program
|
||||
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs");
|
||||
TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs");
|
||||
FluidChunkModelGeneration.fluidChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid2/fluid2.vs", "/Shaders/fluid2/fluid2.fs");
|
||||
//init models
|
||||
assetManager.addModelPathToQueue("Models/unitsphere.fbx");
|
||||
assetManager.addModelPathToQueue("Models/unitsphere_1.fbx");
|
||||
|
||||
@ -18,8 +18,8 @@ import electrosphere.engine.loadingthreads.LoadingThread;
|
||||
import electrosphere.game.config.UserSettings;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.menu.ImGuiWindowMacros;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindowMacros;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
|
||||
|
||||
@ -376,7 +376,10 @@ public class Main {
|
||||
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// G A R B A G E C H E C K
|
||||
///
|
||||
System.gc();
|
||||
|
||||
|
||||
|
||||
|
||||
@ -177,6 +177,10 @@ public class AssetManager {
|
||||
modelsLoadedIntoMemory.put(s,m);
|
||||
}
|
||||
|
||||
public void deregisterModelPath(String path){
|
||||
modelsLoadedIntoMemory.remove(path);
|
||||
}
|
||||
|
||||
public void queueOverrideMeshShader(String modelName, String meshName, String vertPath, String fragPath){
|
||||
MeshShaderOverride override = new MeshShaderOverride(modelName,meshName,vertPath,fragPath);
|
||||
shaderOverrides.add(override);
|
||||
|
||||
@ -15,6 +15,7 @@ import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.server.content.ServerContentManager;
|
||||
import electrosphere.server.fluid.manager.ServerFluidManager;
|
||||
import electrosphere.server.saves.SaveUtils;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.util.FileUtils;
|
||||
@ -56,6 +57,7 @@ public class ArenaLoading {
|
||||
|
||||
private static void initServerArenaTerrainManager(){
|
||||
Globals.serverTerrainManager = ServerTerrainManager.constructArenaTerrainManager();
|
||||
Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager(Globals.serverTerrainManager);
|
||||
}
|
||||
|
||||
private static void initServerArenaWorldData(){
|
||||
|
||||
@ -11,6 +11,7 @@ import electrosphere.audio.AudioUtils;
|
||||
import electrosphere.audio.VirtualAudioSource;
|
||||
import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
|
||||
import electrosphere.client.culling.ClientEntityCullingManager;
|
||||
import electrosphere.client.fluid.cells.FluidCellManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
import electrosphere.client.sim.ClientSimulation;
|
||||
import electrosphere.client.targeting.crosshair.Crosshair;
|
||||
@ -93,6 +94,8 @@ public class ClientLoading {
|
||||
initFoliageManager();
|
||||
//initialize the cell manager (client)
|
||||
initDrawCellManager();
|
||||
//init the fluid cell manager
|
||||
initFluidCellManager();
|
||||
//initialize the basic graphical entities of the world (skybox, camera)
|
||||
initWorldBaseGraphicalEntities();
|
||||
//init arena specific stuff (ie different skybox colors)
|
||||
@ -174,7 +177,6 @@ public class ClientLoading {
|
||||
|
||||
*/
|
||||
Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1));
|
||||
// Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityAirplaneTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1));
|
||||
|
||||
|
||||
|
||||
@ -290,6 +292,41 @@ public class ClientLoading {
|
||||
// System.out.println("Draw Cell Manager ready");
|
||||
}
|
||||
|
||||
static void initFluidCellManager(){
|
||||
while(Globals.clientWorldData == null){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
//initialize draw cell manager
|
||||
Globals.fluidCellManager = new FluidCellManager(Globals.clientTerrainManager, 0, 0, 0);
|
||||
//set our draw cell manager to actually generate drawable chunks
|
||||
Globals.fluidCellManager.setGenerateDrawables(true);
|
||||
//Alerts the client simulation that it should start loading terrain
|
||||
Globals.clientSimulation.setLoadingTerrain(true);
|
||||
//wait for all the terrain data to arrive
|
||||
while(Globals.fluidCellManager.containsUnrequestedCell()){
|
||||
// Globals.drawCellManager.updateInvalidCell();
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// System.out.println("invalid cell");
|
||||
}
|
||||
|
||||
while(Globals.fluidCellManager.containsUndrawableCell()){
|
||||
// Globals.drawCellManager.makeCellDrawable();
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// System.out.println("undrawable");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the entity culling manager
|
||||
*/
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@ -62,16 +62,7 @@ public class CameraEntityUtils {
|
||||
rVal.putData(EntityDataStrings.CAMERA_ORBIT_RADIAL_OFFSET, new Vector3f(0,1,0));
|
||||
rVal.putData(EntityDataStrings.CAMERA_PITCH, 0.0f);
|
||||
rVal.putData(EntityDataStrings.CAMERA_YAW, 0.0f);
|
||||
BehaviorTree entityTrackingTree = new BehaviorTree() {
|
||||
@Override
|
||||
public void simulate(float deltaTime) {
|
||||
if(Globals.playerEntity != null){
|
||||
Vector3d entityPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
CameraEntityUtils.setCameraCenter(rVal, new Vector3f((float)entityPos.x,(float)entityPos.y,(float)entityPos.z).add(getOrbitalCameraRadialOffset(rVal)));
|
||||
}
|
||||
}
|
||||
};
|
||||
Globals.clientScene.registerBehaviorTree(entityTrackingTree);
|
||||
Globals.cameraHandler.setTrackPlayerEntity(true);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
@ -17,9 +17,9 @@ public class FluidChunk {
|
||||
* @param values The values (block types)
|
||||
* @return The fluid chunk entity
|
||||
*/
|
||||
public static Entity clientCreateFluidChunkEntity(float[][][] weights, int[][][] values){
|
||||
public static Entity clientCreateFluidChunkEntity(float[][][] weights){
|
||||
|
||||
FluidChunkModelData data = FluidChunkModelGeneration.generateFluidChunkData(weights, values);
|
||||
FluidChunkModelData data = FluidChunkModelGeneration.generateFluidChunkData(weights);
|
||||
String modelPath = ClientFluidManager.queueFluidGridGeneration(data);
|
||||
|
||||
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
|
||||
@ -29,6 +29,9 @@ public class FluidChunk {
|
||||
// }
|
||||
|
||||
rVal.putData(EntityDataStrings.FLUID_IS_FLUID, true);
|
||||
rVal.putData(EntityDataStrings.DRAW_TRANSPARENT_PASS, true);
|
||||
rVal.removeData(EntityDataStrings.DRAW_SOLID_PASS);
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
package electrosphere.renderer.ui.imgui;
|
||||
package electrosphere.menu;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import electrosphere.audio.VirtualAudioSource;
|
||||
import electrosphere.controls.ControlHandler.ControlsState;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiLinePlot;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiBarPlot;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindow;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
|
||||
import imgui.ImGui;
|
||||
@ -25,24 +30,26 @@ public class ImGuiWindowMacros {
|
||||
private static ImGuiLinePlot globalFrametimePlot;
|
||||
private static Map<String,ImGuiLinePlotDataset> globalFrametimeDatasets;
|
||||
|
||||
//server sim time graph
|
||||
private static ImGuiWindow serverFrametimeWindow;
|
||||
private static ImGuiBarPlot serverFrametimePlot;
|
||||
private static double serverFrametimeTrackerStorage = 0;
|
||||
|
||||
//audio debug menu
|
||||
private static ImGuiWindow audioDebugMenu;
|
||||
private static boolean showAllVirtualAudioChildren = false;
|
||||
private static boolean showMappedVirtualAudioChildren = true;
|
||||
|
||||
//player entity details
|
||||
private static ImGuiWindow playerEntityWindow;
|
||||
|
||||
//fluid details
|
||||
private static ImGuiWindow fluidWindow;
|
||||
|
||||
/**
|
||||
* Initializes imgui windows
|
||||
*/
|
||||
public static void initImGuiWindows(){
|
||||
createMainDebugMenu();
|
||||
createFramerateGraph();
|
||||
createServerFrametimeGraph();
|
||||
createAudioDebugMenu();
|
||||
createPlayerEntityDebugWindow();
|
||||
createFluidDebugWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,46 +94,12 @@ public class ImGuiWindowMacros {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a server frametime breakdown graph
|
||||
*/
|
||||
private static void createServerFrametimeGraph(){
|
||||
serverFrametimeWindow = new ImGuiWindow("Server Frametime Graph");
|
||||
serverFrametimePlot = new ImGuiBarPlot("Server Frametime plot");
|
||||
serverFrametimeWindow.addElement(serverFrametimePlot);
|
||||
serverFrametimeWindow.setOpen(false);
|
||||
RenderingEngine.addImGuiWindow(serverFrametimeWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a tracker for server frametime for a given method
|
||||
*/
|
||||
public static void startServerFrametimeTrackerFlame(double startValue){
|
||||
serverFrametimeTrackerStorage = startValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes tracking a single run of a specific method
|
||||
* @param valueName The name of the method
|
||||
* @param endValue the end time that the method finished on
|
||||
*/
|
||||
public static void clockServerFrametimeTrackerFlame(String valueName, double endValue){
|
||||
serverFrametimePlot.addToDatapoint(valueName, endValue - serverFrametimeTrackerStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the server frametime values so they can be re-filled
|
||||
*/
|
||||
public static void clearServerFrametime(){
|
||||
serverFrametimePlot.clearDatapoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create audio debug menu
|
||||
*/
|
||||
private static void createAudioDebugMenu(){
|
||||
audioDebugMenu = new ImGuiWindow("Audio");
|
||||
audioDebugMenu.callback = new ImGuiWindowCallback() {
|
||||
audioDebugMenu.setCallback(new ImGuiWindowCallback() {
|
||||
@Override
|
||||
public void exec() {
|
||||
//audio engine details
|
||||
@ -175,38 +148,84 @@ public class ImGuiWindowMacros {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
audioDebugMenu.setOpen(false);
|
||||
RenderingEngine.addImGuiWindow(audioDebugMenu);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create player entity debug menu
|
||||
*/
|
||||
private static void createPlayerEntityDebugWindow(){
|
||||
playerEntityWindow = new ImGuiWindow("Player Entity");
|
||||
playerEntityWindow.setCallback(new ImGuiWindowCallback() {
|
||||
@Override
|
||||
public void exec() {
|
||||
//player entity details
|
||||
ImGui.text("Player Entity Details");
|
||||
if(Globals.playerEntity != null){
|
||||
ImGui.text("Position: " + EntityUtils.getPosition(Globals.playerEntity));
|
||||
}
|
||||
if(ImGui.button("Toggle Player Camera Lock")){
|
||||
Globals.cameraHandler.setTrackPlayerEntity(!Globals.cameraHandler.getTrackPlayerEntity());
|
||||
}
|
||||
}
|
||||
});
|
||||
playerEntityWindow.setOpen(false);
|
||||
RenderingEngine.addImGuiWindow(playerEntityWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create fluid debug menu
|
||||
*/
|
||||
private static void createFluidDebugWindow(){
|
||||
fluidWindow = new ImGuiWindow("Fluids");
|
||||
fluidWindow.setCallback(new ImGuiWindowCallback() {
|
||||
@Override
|
||||
public void exec() {
|
||||
//audio engine details
|
||||
ImGui.text("Fluids Debug");
|
||||
if(ImGui.button("Toggle Simulation")){
|
||||
Globals.serverFluidManager.setSimulate(!Globals.serverFluidManager.getSimulate());;
|
||||
}
|
||||
}
|
||||
});
|
||||
fluidWindow.setOpen(false);
|
||||
RenderingEngine.addImGuiWindow(fluidWindow);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inits the main debug menu
|
||||
*/
|
||||
private static void createMainDebugMenu(){
|
||||
mainDebugWindow = new ImGuiWindow("Debug");
|
||||
mainDebugWindow.callback = new ImGuiWindowCallback() {
|
||||
mainDebugWindow.setCallback(new ImGuiWindowCallback() {
|
||||
@Override
|
||||
public void exec() {
|
||||
//show global framerate line graph
|
||||
if(ImGui.button("Show Overall Frametime")){
|
||||
globalFrametimeWindow.setOpen(true);
|
||||
}
|
||||
//show server frametime bar graph
|
||||
if(ImGui.button("Show Server Frametime Breakdown")){
|
||||
serverFrametimeWindow.setOpen(true);
|
||||
}
|
||||
//show audio debug
|
||||
if(ImGui.button("Show Audio Debug Menu")){
|
||||
audioDebugMenu.setOpen(true);
|
||||
}
|
||||
//show audio debug
|
||||
if(ImGui.button("Show Player Entity Debug Menu")){
|
||||
playerEntityWindow.setOpen(true);
|
||||
}
|
||||
//show fluids debug
|
||||
if(ImGui.button("Show Fluids Debug Menu")){
|
||||
fluidWindow.setOpen(true);
|
||||
}
|
||||
//close button
|
||||
if(ImGui.button("Close")){
|
||||
mainDebugWindow.setOpen(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
RenderingEngine.addImGuiWindow(mainDebugWindow);
|
||||
}
|
||||
|
||||
@ -47,7 +47,11 @@ public class MenuGeneratorsInGame {
|
||||
div.setOnNavigationCallback(new NavigationEventCallback() {public boolean execute(NavigationEvent event){
|
||||
WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), false);
|
||||
Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN);
|
||||
if(Globals.cameraHandler.getTrackPlayerEntity()){
|
||||
Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME);
|
||||
} else {
|
||||
Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_FREE_CAMERA);
|
||||
}
|
||||
Globals.controlHandler.hideMouse();
|
||||
return false;
|
||||
}});
|
||||
@ -68,7 +72,11 @@ public class MenuGeneratorsInGame {
|
||||
backButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
|
||||
WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), false);
|
||||
Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN);
|
||||
if(Globals.cameraHandler.getTrackPlayerEntity()){
|
||||
Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME);
|
||||
} else {
|
||||
Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_FREE_CAMERA);
|
||||
}
|
||||
Globals.controlHandler.hideMouse();
|
||||
return false;
|
||||
}});
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.net.client.protocol;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import electrosphere.client.fluid.cache.FluidChunkData;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.collision.CollisionWorldData;
|
||||
@ -49,6 +50,12 @@ public class TerrainProtocol {
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SENDFLUIDDATA: {
|
||||
Globals.clientFluidManager.attachFluidMessage(message);
|
||||
} break;
|
||||
case UPDATEFLUIDDATA: {
|
||||
Globals.clientFluidManager.attachFluidMessage(message);
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerNetworking.WARNING("Client networking: Unhandled message of type: " + message.getMessageSubtype());
|
||||
break;
|
||||
|
||||
@ -236,6 +236,21 @@ SYNCHRONIZATION_MESSAGE,
|
||||
rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
|
||||
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
|
||||
rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
|
||||
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
|
||||
rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
|
||||
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
|
||||
rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TypeBytes.MESSAGE_TYPE_SERVER:
|
||||
|
||||
@ -17,6 +17,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
SPAWNPOSITION,
|
||||
REQUESTCHUNKDATA,
|
||||
SENDCHUNKDATA,
|
||||
REQUESTFLUIDDATA,
|
||||
SENDFLUIDDATA,
|
||||
UPDATEFLUIDDATA,
|
||||
}
|
||||
|
||||
TerrainMessageType messageType;
|
||||
@ -266,6 +269,16 @@ public class TerrainMessage extends NetworkMessage {
|
||||
}
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
|
||||
return TerrainMessage.canParsesendChunkDataMessage(byteBuffer);
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
|
||||
if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
|
||||
return TerrainMessage.canParsesendFluidDataMessage(byteBuffer);
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
|
||||
return TerrainMessage.canParseupdateFluidDataMessage(byteBuffer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -488,6 +501,120 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
rVal.setworldY(worldY);
|
||||
rVal.setworldZ(worldZ);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList();
|
||||
if(currentStreamLength < 6){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 10){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 14){
|
||||
return false;
|
||||
}
|
||||
int chunkDataSize = 0;
|
||||
if(currentStreamLength < 18){
|
||||
return false;
|
||||
} else {
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 0));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 1));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 2));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 3));
|
||||
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
|
||||
}
|
||||
if(currentStreamLength < 18 + chunkDataSize){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
rVal.setworldY(worldY);
|
||||
rVal.setworldZ(worldZ);
|
||||
rVal.setchunkData(chunkData);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList();
|
||||
if(currentStreamLength < 6){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 10){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 14){
|
||||
return false;
|
||||
}
|
||||
int chunkDataSize = 0;
|
||||
if(currentStreamLength < 18){
|
||||
return false;
|
||||
} else {
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 0));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 1));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 2));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 3));
|
||||
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
|
||||
}
|
||||
if(currentStreamLength < 18 + chunkDataSize){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
rVal.setworldY(worldY);
|
||||
rVal.setworldZ(worldZ);
|
||||
rVal.setchunkData(chunkData);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
void serialize(){
|
||||
byte[] intValues = new byte[8];
|
||||
@ -718,6 +845,77 @@ public class TerrainMessage extends NetworkMessage {
|
||||
rawBytes[18+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
case REQUESTFLUIDDATA:
|
||||
rawBytes = new byte[2+4+4+4];
|
||||
//message header
|
||||
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
|
||||
//entity messaage header
|
||||
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA;
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[2+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[6+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[10+i] = intValues[i];
|
||||
}
|
||||
break;
|
||||
case SENDFLUIDDATA:
|
||||
rawBytes = new byte[2+4+4+4+4+chunkData.length];
|
||||
//message header
|
||||
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
|
||||
//entity messaage header
|
||||
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA;
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[2+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[6+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[10+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[14+i] = intValues[i];
|
||||
}
|
||||
for(int i = 0; i < chunkData.length; i++){
|
||||
rawBytes[18+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
case UPDATEFLUIDDATA:
|
||||
rawBytes = new byte[2+4+4+4+4+chunkData.length];
|
||||
//message header
|
||||
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
|
||||
//entity messaage header
|
||||
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA;
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[2+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[6+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[10+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[14+i] = intValues[i];
|
||||
}
|
||||
for(int i = 0; i < chunkData.length; i++){
|
||||
rawBytes[18+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
serialized = true;
|
||||
}
|
||||
|
||||
@ -88,6 +88,9 @@ Message categories
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION = 6;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA = 7;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA = 8;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 9;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 10;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 11;
|
||||
/*
|
||||
Terrain packet sizes
|
||||
*/
|
||||
@ -99,6 +102,7 @@ Message categories
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE_SIZE = 38;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION_SIZE = 26;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE = 14;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE = 14;
|
||||
/*
|
||||
Server subcategories
|
||||
*/
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package electrosphere.net.parser.net.raw;
|
||||
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.NetworkMessage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -52,7 +51,7 @@ public class NetworkParser {
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
LoggerInterface.loggerNetworking.ERROR("", ex);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import electrosphere.net.server.Server;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.terrain.editing.TerrainEditing;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.models.TerrainModification;
|
||||
@ -38,11 +39,18 @@ public class TerrainProtocol {
|
||||
case REQUESTUSETERRAINPALETTE: {
|
||||
attemptUseTerrainEditPalette(connectionHandler, message);
|
||||
} break;
|
||||
case REQUESTFLUIDDATA: {
|
||||
sendWorldFluidSubChunk(connectionHandler,
|
||||
message.getworldX(), message.getworldY(), message.getworldZ()
|
||||
);
|
||||
} break;
|
||||
//all ignored message types
|
||||
case UPDATEFLUIDDATA:
|
||||
case RESPONSEMETADATA:
|
||||
case SPAWNPOSITION:
|
||||
case UPDATEVOXEL:
|
||||
case SENDCHUNKDATA:
|
||||
case SENDFLUIDDATA:
|
||||
//silently ignore
|
||||
break;
|
||||
}
|
||||
@ -194,6 +202,20 @@ public class TerrainProtocol {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
static void sendWorldFluidSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
|
||||
|
||||
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
|
||||
|
||||
ServerFluidChunk chunk = Globals.serverFluidManager.getChunk(worldX, worldY, worldZ);
|
||||
|
||||
|
||||
ByteBuffer buffer = constructFluidByteBuffer(chunk);
|
||||
|
||||
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array()));
|
||||
|
||||
}
|
||||
|
||||
static void sendWorldMetadata(ServerConnectionHandler connectionHandler){
|
||||
//world metadata
|
||||
connectionHandler.addMessagetoOutgoingQueue(
|
||||
@ -229,4 +251,55 @@ public class TerrainProtocol {
|
||||
TerrainEditing.editTerrain(realm, location, message.getvalue(), message.getterrainValue(), message.getterrainWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a buffer to send a fluid chunk to the client
|
||||
* @param chunk The chunk to send
|
||||
* @return the buffer
|
||||
*/
|
||||
public static ByteBuffer constructFluidByteBuffer(ServerFluidChunk chunk){
|
||||
//The length along each access of the chunk data. Typically, should be at least 17.
|
||||
//Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate
|
||||
//chunk data that connects seamlessly to the next chunk.
|
||||
int xWidth = chunk.getWeights().length;
|
||||
int yWidth = chunk.getWeights()[0].length;
|
||||
int zWidth = chunk.getWeights()[0][0].length;
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(xWidth*yWidth*zWidth*(4+4+4+4));
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
|
||||
for(int x = 0; x < xWidth; x++){
|
||||
for(int y = 0; y < yWidth; y++){
|
||||
for(int z = 0; z < zWidth; z++){
|
||||
floatView.put(chunk.getWeights()[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < xWidth; x++){
|
||||
for(int y = 0; y < yWidth; y++){
|
||||
for(int z = 0; z < zWidth; z++){
|
||||
floatView.put(chunk.getVelocityX()[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < xWidth; x++){
|
||||
for(int y = 0; y < yWidth; y++){
|
||||
for(int z = 0; z < zWidth; z++){
|
||||
floatView.put(chunk.getVelocityY()[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < xWidth; x++){
|
||||
for(int y = 0; y < yWidth; y++){
|
||||
for(int z = 0; z < zWidth; z++){
|
||||
floatView.put(chunk.getVelocityZ()[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ import electrosphere.renderer.model.Material;
|
||||
import electrosphere.renderer.model.Mesh;
|
||||
import electrosphere.renderer.model.Model;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
|
||||
@ -559,7 +560,7 @@ public class FluidChunkModelGeneration {
|
||||
return new Vector3f(x,y,z);
|
||||
}
|
||||
|
||||
public static FluidChunkModelData generateFluidChunkData(float[][][] weightGrid, int[][][] typeGrid){
|
||||
public static FluidChunkModelData generateFluidChunkData(float[][][] weightGrid){
|
||||
|
||||
// 5 6
|
||||
// +-------------+ +-----5-------+ ^ Y
|
||||
@ -601,7 +602,7 @@ public class FluidChunkModelGeneration {
|
||||
weightGrid[x+0][y+1][z+0], weightGrid[x+0][y+1][z+1], weightGrid[x+1][y+1][z+1], weightGrid[x+1][y+1][z+0]
|
||||
);
|
||||
//polygonize the current gridcell
|
||||
polygonize(currentCell, 0, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
polygonize(currentCell, 0.01f, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -768,7 +769,13 @@ public class FluidChunkModelGeneration {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
float halfChunk = ServerTerrainChunk.CHUNK_DIMENSION / 2.0f;
|
||||
mesh.updateBoundingSphere(
|
||||
halfChunk,
|
||||
halfChunk,
|
||||
halfChunk,
|
||||
(float)Math.sqrt(halfChunk * halfChunk + halfChunk * halfChunk + halfChunk * halfChunk)
|
||||
);
|
||||
|
||||
|
||||
glBindVertexArray(0);
|
||||
@ -796,6 +803,7 @@ public class FluidChunkModelGeneration {
|
||||
m.setParent(rVal);
|
||||
|
||||
rVal.getMeshes().add(m);
|
||||
rVal.setBoundingSphere(m.getBoundingSphere());
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -607,7 +607,7 @@ public class TerrainChunkModelGeneration {
|
||||
terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0]
|
||||
);
|
||||
//polygonize the current gridcell
|
||||
polygonize(currentCell, 0, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
polygonize(currentCell, 0.01f, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,15 +447,18 @@ public class Mesh {
|
||||
}
|
||||
|
||||
if(renderPipelineState.getInstanced()){
|
||||
if(renderPipelineState.getInstanceData()!=null){
|
||||
InstanceData instanceData = renderPipelineState.getInstanceData();
|
||||
Map<ShaderAttribute,Object> buffers = instanceData.getCpuBufferMap();
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> 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);
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -202,6 +202,31 @@ public class Texture {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
// openGLState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
||||
|
||||
@ -17,10 +17,13 @@ import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.net.server.protocol.TerrainProtocol;
|
||||
import electrosphere.server.content.ServerContentManager;
|
||||
import electrosphere.server.datacell.interfaces.DataCellManager;
|
||||
import electrosphere.server.datacell.interfaces.VoxelCellManager;
|
||||
import electrosphere.server.datacell.physics.PhysicsDataCell;
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.fluid.manager.ServerFluidManager;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
@ -43,6 +46,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
Realm parent;
|
||||
//Manager for terrain for this particular cell manager
|
||||
ServerTerrainManager serverTerrainManager;
|
||||
//manager for fluids for this particular cell manager
|
||||
ServerFluidManager serverFluidManager;
|
||||
//lock for terrain editing
|
||||
Semaphore terrainEditLock = new Semaphore(1);
|
||||
//manager for getting entities to fill in a cell
|
||||
@ -52,9 +57,15 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
* Constructor
|
||||
* @param parent The gridded data cell manager's parent realm
|
||||
*/
|
||||
public GriddedDataCellManager(Realm parent, ServerTerrainManager serverTerrainManager, ServerContentManager serverContentManager) {
|
||||
public GriddedDataCellManager(
|
||||
Realm parent,
|
||||
ServerTerrainManager serverTerrainManager,
|
||||
ServerFluidManager serverFluidManager,
|
||||
ServerContentManager serverContentManager
|
||||
) {
|
||||
this.parent = parent;
|
||||
this.serverTerrainManager = serverTerrainManager;
|
||||
this.serverFluidManager = serverFluidManager;
|
||||
this.serverContentManager = serverContentManager;
|
||||
}
|
||||
|
||||
@ -343,6 +354,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
for(ServerDataCell cell : loadedCells){
|
||||
Globals.microSimulation.simulate(cell, parent.getHitboxManager());
|
||||
|
||||
//simulate fluid
|
||||
Vector3i cellPos = this.getCellWorldPosition(cell);
|
||||
boolean update = this.serverFluidManager.simulate(cellPos.x,cellPos.y,cellPos.z);
|
||||
if(update){
|
||||
rebroadcastFluidChunk(cellPos);
|
||||
}
|
||||
}
|
||||
loadedCellsLock.release();
|
||||
updatePlayerPositions();
|
||||
@ -357,6 +375,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
return serverTerrainManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server fluid manager for this realm if it exists
|
||||
* @return The server fluid manager if it exists, null otherwise
|
||||
*/
|
||||
public ServerFluidManager getServerFluidManager(){
|
||||
return serverFluidManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs code to generate physics entities and register cell in a dedicated thread.
|
||||
* Because cell hasn't been registered yet, no simulation is performed until the physics is created.
|
||||
@ -461,4 +487,24 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
return cellPositionMap.get(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Gets the fluid chunk at a given position
|
||||
*/
|
||||
public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition) {
|
||||
return serverFluidManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebroadcasts the fluid cell at a given position
|
||||
* @param worldPosition the world position
|
||||
*/
|
||||
private void rebroadcastFluidChunk(Vector3i worldPosition){
|
||||
ServerDataCell cell = getCellAtWorldPosition(worldPosition);
|
||||
ServerFluidChunk chunk = getFluidChunkAtPosition(worldPosition);
|
||||
cell.broadcastNetworkMessage(
|
||||
TerrainMessage.constructupdateFluidDataMessage(worldPosition.x, worldPosition.y, worldPosition.z, TerrainProtocol.constructFluidByteBuffer(chunk).array())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@ import electrosphere.engine.Main;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.Scene;
|
||||
import electrosphere.entity.types.hitbox.HitboxManager;
|
||||
import electrosphere.menu.ImGuiWindowMacros;
|
||||
import electrosphere.net.parser.net.message.NetworkMessage;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindowMacros;
|
||||
import electrosphere.server.datacell.interfaces.DataCellManager;
|
||||
|
||||
import java.util.HashSet;
|
||||
@ -156,15 +156,10 @@ public class Realm {
|
||||
* Tells the data cell manager to simulate all loaded cells
|
||||
*/
|
||||
protected void simulate(){
|
||||
ImGuiWindowMacros.clearServerFrametime();
|
||||
//simulate bullet physics engine step
|
||||
ImGuiWindowMacros.startServerFrametimeTrackerFlame(System.currentTimeMillis());
|
||||
collisionEngine.simulatePhysics((float)Globals.timekeeper.getSimFrameTime());
|
||||
ImGuiWindowMacros.clockServerFrametimeTrackerFlame("physics", System.currentTimeMillis());
|
||||
//main simulation
|
||||
ImGuiWindowMacros.startServerFrametimeTrackerFlame(System.currentTimeMillis());
|
||||
dataCellManager.simulate();
|
||||
ImGuiWindowMacros.clockServerFrametimeTrackerFlame("simulate", System.currentTimeMillis());
|
||||
//data cell manager update misc variables (player positions, unload not-in-use cells)
|
||||
if(dataCellManager != null){
|
||||
dataCellManager.unloadPlayerlessChunks();
|
||||
|
||||
@ -53,7 +53,7 @@ public class RealmManager {
|
||||
//create realm
|
||||
Realm realm = new Realm(collisionEngine, new HitboxManager());
|
||||
//create function classes
|
||||
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverContentManager);
|
||||
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverFluidManager,Globals.serverContentManager);
|
||||
EntityDataCellMapper entityDataCellMapper = new EntityDataCellMapper();
|
||||
//init gridded manager
|
||||
griddedDataCellManager.init(serverWorldData);
|
||||
|
||||
@ -2,6 +2,7 @@ package electrosphere.server.datacell.interfaces;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
@ -41,4 +42,12 @@ public interface VoxelCellManager {
|
||||
*/
|
||||
public void editChunk(Vector3i worldPosition, Vector3i voxelPosition, float weight, int type);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the fluid chunk at a given world position
|
||||
* @param worldPosition The world position
|
||||
* @return the fluid chunk
|
||||
*/
|
||||
public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,217 @@
|
||||
package electrosphere.server.fluid.diskmap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.DeflaterInputStream;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterOutputStream;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.server.Server;
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.util.FileUtils;
|
||||
|
||||
/**
|
||||
* An interface for accessing the disk map of chunk information
|
||||
*/
|
||||
public class FluidDiskMap {
|
||||
|
||||
//The map of world position+chunk type to the file that actually houses that information
|
||||
Map<String,String> worldPosFileMap = new HashMap<String,String>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public FluidDiskMap(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for a given chunk file based on a world coordinate
|
||||
* @param worldX The x component
|
||||
* @param worldY The y component
|
||||
* @param worldZ The z component
|
||||
* @return The key
|
||||
*/
|
||||
private static String getFluidChunkKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ + "f";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a diskmap based on a given save name
|
||||
* @param saveName The save name
|
||||
*/
|
||||
public void init(String saveName){
|
||||
LoggerInterface.loggerEngine.DEBUG("INIT CHUNK MAP " + saveName);
|
||||
if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){
|
||||
worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "fluid.map", Map.class);
|
||||
LoggerInterface.loggerEngine.DEBUG("POS FILE MAP: " + worldPosFileMap.keySet());
|
||||
} else {
|
||||
worldPosFileMap = new HashMap<String,String>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the disk map to disk
|
||||
*/
|
||||
public void save(){
|
||||
FileUtils.serializeObjectToSavePath(Globals.currentSaveName, "fluid.map", worldPosFileMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the map contains a given chunk position
|
||||
* @param worldX The x component
|
||||
* @param worldY The y component
|
||||
* @param worldZ The z component
|
||||
* @return True if the map contains the chunk, false otherwise
|
||||
*/
|
||||
public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){
|
||||
return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server fluid chunk from disk if it exists, otherwise returns null
|
||||
* @param worldX The x coordinate
|
||||
* @param worldY The y coordinate
|
||||
* @param worldZ The z coordinate
|
||||
* @return The server fluid chunk if it exists, null otherwise
|
||||
*/
|
||||
public ServerFluidChunk getFluidChunk(int worldX, int worldY, int worldZ){
|
||||
LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ);
|
||||
ServerFluidChunk rVal = null;
|
||||
if(containsFluidAtPosition(worldX, worldY, worldZ)){
|
||||
//read file
|
||||
String fileName = worldPosFileMap.get(getFluidChunkKey(worldX, worldY, worldZ));
|
||||
byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSaveName, fileName);
|
||||
//decompress
|
||||
byte[] rawData = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out);
|
||||
try {
|
||||
inflaterInputStream.write(rawDataCompressed);
|
||||
inflaterInputStream.flush();
|
||||
inflaterInputStream.close();
|
||||
rawData = out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
//parse
|
||||
if(rawData != null){
|
||||
ByteBuffer buffer = ByteBuffer.wrap(rawData);
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
float[][][] weights = new float[DIM][DIM][DIM];
|
||||
float[][][] velocityX = new float[DIM][DIM][DIM];
|
||||
float[][][] velocityY = new float[DIM][DIM][DIM];
|
||||
float[][][] velocityZ = new float[DIM][DIM][DIM];
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
weights[x][y][z] = floatView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
velocityX[x][y][z] = floatView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
velocityY[x][y][z] = floatView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
velocityZ[x][y][z] = floatView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
rVal = new ServerFluidChunk(worldX, worldY, worldZ, weights, velocityX, velocityY, velocityZ);
|
||||
}
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a fluid chunk to disk
|
||||
* @param fluidChunk The fluid chunk
|
||||
*/
|
||||
public void saveToDisk(ServerFluidChunk fluidChunk){
|
||||
LoggerInterface.loggerEngine.DEBUG("Save to disk: " + fluidChunk.getWorldX() + " " + fluidChunk.getWorldY() + " " + fluidChunk.getWorldZ());
|
||||
//get the file name for this chunk
|
||||
String fileName = null;
|
||||
String chunkKey = getFluidChunkKey(fluidChunk.getWorldX(),fluidChunk.getWorldY(),fluidChunk.getWorldZ());
|
||||
if(worldPosFileMap.containsKey(chunkKey)){
|
||||
fileName = worldPosFileMap.get(chunkKey);
|
||||
} else {
|
||||
fileName = chunkKey + ".dat";
|
||||
}
|
||||
//generate binary for the file
|
||||
float[][][] weights = fluidChunk.getWeights();
|
||||
float[][][] velocityX = fluidChunk.getVelocityX();
|
||||
float[][][] velocityY = fluidChunk.getVelocityY();
|
||||
float[][][] velocityZ = fluidChunk.getVelocityZ();
|
||||
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4);
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
floatView.put(weights[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
floatView.put(velocityX[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
floatView.put(velocityY[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
floatView.put(velocityZ[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//compress
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
DeflaterOutputStream deflaterInputStream = new DeflaterOutputStream(out);
|
||||
try {
|
||||
deflaterInputStream.write(buffer.array());
|
||||
deflaterInputStream.flush();
|
||||
deflaterInputStream.close();
|
||||
//write to disk
|
||||
FileUtils.saveBinaryToSavePath(Globals.currentSaveName, fileName, out.toByteArray());
|
||||
//save to the map of filenames
|
||||
worldPosFileMap.put(chunkKey,fileName);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package electrosphere.server.fluid.generation;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.fluid.models.FluidModel;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
public class ArenaFluidGenerator implements FluidGenerator {
|
||||
|
||||
@Override
|
||||
public ServerFluidChunk generateChunk(int worldX, int worldY, int worldZ) {
|
||||
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
float[][][] velocityX = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
float[][][] velocityY = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
float[][][] velocityZ = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
ServerFluidChunk chunk = new ServerFluidChunk(worldX, worldY, worldZ, weights, velocityX, velocityY, velocityZ);
|
||||
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
weights[x][y][z] = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModel(FluidModel model) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'setModel'");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package electrosphere.server.fluid.generation;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.fluid.models.FluidModel;
|
||||
|
||||
/**
|
||||
* Generates fluid
|
||||
*/
|
||||
public interface FluidGenerator {
|
||||
|
||||
/**
|
||||
* Generates a chunk given an x, y, and z
|
||||
* @param worldX The x component
|
||||
* @param worldY The y component
|
||||
* @param worldZ The z component
|
||||
* @return The chunk
|
||||
*/
|
||||
public ServerFluidChunk generateChunk(int worldX, int worldY, int worldZ);
|
||||
|
||||
/**
|
||||
* Sets the fluid model for the generation algorithm
|
||||
* @param model The fluid model
|
||||
*/
|
||||
public void setModel(FluidModel model);
|
||||
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
package electrosphere.server.fluid.manager;
|
||||
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
|
||||
/**
|
||||
* Is a single chunk of terrain on the server
|
||||
*/
|
||||
public class ServerFluidChunk {
|
||||
|
||||
|
||||
int worldX, worldY, worldZ;
|
||||
float[][][] weights;
|
||||
float[][][] velocityX;
|
||||
float[][][] velocityY;
|
||||
float[][][] velocityZ;
|
||||
|
||||
public ServerFluidChunk(
|
||||
int worldX,
|
||||
int worldY,
|
||||
int worldZ,
|
||||
float[][][] weights,
|
||||
float[][][] velocityX,
|
||||
float[][][] velocityY,
|
||||
float[][][] velocityZ
|
||||
) {
|
||||
this.worldX = worldX;
|
||||
this.worldY = worldY;
|
||||
this.worldZ = worldZ;
|
||||
this.weights = weights;
|
||||
this.velocityX = velocityX;
|
||||
this.velocityY = velocityY;
|
||||
this.velocityZ = velocityZ;
|
||||
}
|
||||
|
||||
public int getWorldX() {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
public int getWorldY() {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
public int getWorldZ() {
|
||||
return worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the world position of this terrain chunk as a joml Vector
|
||||
* @return The vector
|
||||
*/
|
||||
public Vector3i getWorldPosition(){
|
||||
return new Vector3i(worldX,worldY,worldZ);
|
||||
}
|
||||
|
||||
public float[][][] getWeights() {
|
||||
return weights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight of a voxel at a poisiton
|
||||
* @param localPosition The local position
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public float getWeight(Vector3i localPosition){
|
||||
return getWeight(localPosition.x,localPosition.y,localPosition.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight of a voxel at a poisiton
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param z The z coordinate
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public float getWeight(int x, int y, int z){
|
||||
return weights[x][y][z];
|
||||
}
|
||||
|
||||
//get velocity x
|
||||
public float[][][] getVelocityX() {
|
||||
return velocityX;
|
||||
}
|
||||
|
||||
|
||||
//set velocity x
|
||||
public void setVelocityX(float[][][] velocityX) {
|
||||
this.velocityX = velocityX;
|
||||
}
|
||||
|
||||
//get velocity y
|
||||
public float[][][] getVelocityY() {
|
||||
return velocityY;
|
||||
}
|
||||
|
||||
//set velocity y
|
||||
public void setVelocityY(float[][][] velocityY) {
|
||||
this.velocityY = velocityY;
|
||||
}
|
||||
|
||||
|
||||
//get velocity z
|
||||
public float[][][] getVelocityZ() {
|
||||
return velocityZ;
|
||||
}
|
||||
|
||||
//set velocity z
|
||||
public void setVelocityZ(float[][][] velocityZ) {
|
||||
this.velocityZ = velocityZ;
|
||||
}
|
||||
|
||||
//get a velocity at a given x, y and z as a Vector3f
|
||||
public Vector3f getVelocity(int x, int y, int z){
|
||||
return new Vector3f(velocityX[x][y][z],velocityY[x][y][z],velocityZ[x][y][z]);
|
||||
}
|
||||
|
||||
//set a velocity at a given x, y, and z given three ints
|
||||
public void setVelocity(int x, int y, int z, float velX, float velY, float velZ){
|
||||
velocityX[x][y][z] = velX;
|
||||
velocityY[x][y][z] = velY;
|
||||
velocityZ[x][y][z] = velZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,320 @@
|
||||
package electrosphere.server.fluid.manager;
|
||||
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.server.fluid.diskmap.FluidDiskMap;
|
||||
import electrosphere.server.fluid.generation.ArenaFluidGenerator;
|
||||
import electrosphere.server.fluid.generation.FluidGenerator;
|
||||
import electrosphere.server.fluid.models.FluidModel;
|
||||
import electrosphere.server.fluid.simulator.ServerFluidSimulator;
|
||||
import electrosphere.server.fluid.simulator.cellularautomata.FluidCellularAutomataSimulator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.util.FileUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
/**
|
||||
* Provides an interface for the server to query information about fluid
|
||||
*/
|
||||
public class ServerFluidManager {
|
||||
|
||||
//The size of the world in discrete units * must be multiple of 200
|
||||
int worldSizeDiscrete;
|
||||
|
||||
//The vertical multiplier applied to the statically generated fluid
|
||||
int verticalInterpolationRatio;
|
||||
|
||||
float interpolationRandomDampener;
|
||||
|
||||
long seed;
|
||||
|
||||
//The model of the fluid this manager is managing
|
||||
FluidModel model;
|
||||
|
||||
|
||||
//In memory cache of chunk data
|
||||
//Basic idea is we associate string that contains chunk x&y&z with elevation
|
||||
//While we incur a penalty with converting ints -> string, think this will
|
||||
//offset regenerating the array every time we want a new one
|
||||
int cacheSize = 500;
|
||||
Map<String, ServerFluidChunk> chunkCache;
|
||||
List<String> chunkCacheContents;
|
||||
|
||||
//The map of chunk position <-> file on disk containing chunk data
|
||||
FluidDiskMap chunkDiskMap = null;
|
||||
|
||||
//The generation algorithm for this fluid manager
|
||||
FluidGenerator chunkGenerator;
|
||||
|
||||
//the fluid simulator
|
||||
ServerFluidSimulator serverFluidSimulator;
|
||||
|
||||
//the terrain manager associated
|
||||
ServerTerrainManager serverTerrainManager;
|
||||
|
||||
//controls whether fluid simulation should actually happen or not
|
||||
boolean simulate = true;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ServerFluidManager(
|
||||
ServerTerrainManager serverTerrainManager,
|
||||
int worldSizeDiscrete,
|
||||
int verticalInterpolationRatio,
|
||||
float interpolationRandomDampener,
|
||||
long seed,
|
||||
FluidGenerator chunkGenerator
|
||||
){
|
||||
this.serverTerrainManager = serverTerrainManager;
|
||||
this.worldSizeDiscrete = worldSizeDiscrete;
|
||||
this.verticalInterpolationRatio = verticalInterpolationRatio;
|
||||
this.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
|
||||
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
this.interpolationRandomDampener = interpolationRandomDampener;
|
||||
this.seed = seed;
|
||||
this.chunkGenerator = chunkGenerator;
|
||||
}
|
||||
|
||||
ServerFluidManager(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an arena fluid manager
|
||||
* @return The arena fluid manager
|
||||
*/
|
||||
public static ServerFluidManager constructArenaFluidManager(ServerTerrainManager serverTerrainManager){
|
||||
ServerFluidManager rVal = new ServerFluidManager();
|
||||
rVal.serverTerrainManager = serverTerrainManager;
|
||||
rVal.worldSizeDiscrete = 2;
|
||||
rVal.verticalInterpolationRatio = 0;
|
||||
rVal.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
|
||||
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
rVal.interpolationRandomDampener = 0.0f;
|
||||
rVal.chunkGenerator = new ArenaFluidGenerator();
|
||||
rVal.serverFluidSimulator = new FluidCellularAutomataSimulator();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a fluid model for the manager
|
||||
*/
|
||||
public void generate(){
|
||||
// FluidGenerator terrainGen = new FluidGenerator();
|
||||
// terrainGen.setInterpolationRatio(worldSizeDiscrete/200);
|
||||
// terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio);
|
||||
// terrainGen.setRandomSeed(seed);
|
||||
// model = terrainGen.generateModel();
|
||||
// this.chunkGenerator.setModel(model);
|
||||
// model.setInterpolationRandomDampener(interpolationRandomDampener);
|
||||
// this.chunkDiskMap = new ChunkDiskMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the fluid model backing this manager to a save file
|
||||
* @param saveName The name of the save
|
||||
*/
|
||||
public void save(String saveName){
|
||||
ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4);
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < model.getElevation().length; x++){
|
||||
floatView.put(model.getElevation()[x]);
|
||||
}
|
||||
floatView.flip();
|
||||
FileUtils.saveBinaryToSavePath(saveName, "./fluid.dat", buffer.array());
|
||||
FileUtils.serializeObjectToSavePath(saveName, "./fluid.json", model);
|
||||
//for each chunk, save via disk map
|
||||
for(String chunkKey : chunkCacheContents){
|
||||
ServerFluidChunk chunk = chunkCache.get(chunkKey);
|
||||
chunkDiskMap.saveToDisk(chunk);
|
||||
}
|
||||
//save disk map itself
|
||||
if(chunkDiskMap != null){
|
||||
chunkDiskMap.save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a fluid manager from a save file
|
||||
* @param saveName The name of the save
|
||||
*/
|
||||
public void load(String saveName){
|
||||
//load fluid model
|
||||
model = FileUtils.loadObjectFromSavePath(saveName, "./fluid.json", FluidModel.class);
|
||||
chunkGenerator.setModel(model);
|
||||
byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./fluid.dat");
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
float[][] elevation = new float[Globals.serverWorldData.getWorldSizeDiscrete()][Globals.serverWorldData.getWorldSizeDiscrete()];
|
||||
for(int x = 0; x < Globals.serverWorldData.getWorldSizeDiscrete(); x++){
|
||||
for(int y = 0; y < Globals.serverWorldData.getWorldSizeDiscrete(); y++){
|
||||
elevation[x][y] = floatView.get();
|
||||
}
|
||||
}
|
||||
model.setElevationArray(elevation);
|
||||
//load chunk disk map
|
||||
chunkDiskMap = new FluidDiskMap();
|
||||
chunkDiskMap.init(saveName);
|
||||
}
|
||||
|
||||
public float[][] getFluidAtChunk(int x, int y){
|
||||
return model.getElevationForChunk(x, y);
|
||||
}
|
||||
|
||||
public double getHeightAtPosition(double x, double y, double z){
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWorldDiscreteSize(){
|
||||
return worldSizeDiscrete;
|
||||
}
|
||||
|
||||
public float getDiscreteValue(int x, int y){
|
||||
if(model != null){
|
||||
return model.getElevation()[x][y];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getDynamicInterpolationRatio(){
|
||||
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
||||
if(model != null){
|
||||
return model.getDynamicInterpolationRatio();
|
||||
} else {
|
||||
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public float getRandomDampener(){
|
||||
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
||||
if(model != null){
|
||||
return model.getRandomDampener();
|
||||
} else {
|
||||
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fluid model backing this fluid manager
|
||||
* @return The fluid model
|
||||
*/
|
||||
public FluidModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key for a given world position
|
||||
* @param worldX The x component
|
||||
* @param worldY The y component
|
||||
* @param worldZ The z component
|
||||
* @return The key
|
||||
*/
|
||||
public String getKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a server fluid chunk
|
||||
* @param worldX The world x position
|
||||
* @param worldY The world y position
|
||||
* @param worldZ The world z position
|
||||
* @return The ServerFluidChunk
|
||||
*/
|
||||
public ServerFluidChunk getChunk(int worldX, int worldY, int worldZ){
|
||||
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
||||
String key = getKey(worldX,worldY,worldZ);
|
||||
ServerFluidChunk returnedChunk = null;
|
||||
if(chunkCache.containsKey(key)){
|
||||
chunkCacheContents.remove(key);
|
||||
chunkCacheContents.add(0, key);
|
||||
returnedChunk = chunkCache.get(key);
|
||||
return returnedChunk;
|
||||
} else {
|
||||
if(chunkCacheContents.size() > cacheSize){
|
||||
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
|
||||
chunkCache.remove(oldChunk);
|
||||
}
|
||||
//pull from disk if it exists
|
||||
if(chunkDiskMap != null){
|
||||
if(chunkDiskMap.containsFluidAtPosition(worldX, worldY, worldZ)){
|
||||
returnedChunk = chunkDiskMap.getFluidChunk(worldX, worldY, worldZ);
|
||||
}
|
||||
}
|
||||
//generate if it does not exist
|
||||
if(returnedChunk == null){
|
||||
returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
|
||||
}
|
||||
chunkCache.put(key, returnedChunk);
|
||||
chunkCacheContents.add(key);
|
||||
return returnedChunk;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a given position's chunk to disk.
|
||||
* Uses the current global save name
|
||||
* @param position The position to save
|
||||
*/
|
||||
public void savePositionToDisk(Vector3i position){
|
||||
chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a deform to fluid at a given location
|
||||
* @param worldPos The world coordinates of the chunk to modify
|
||||
* @param voxelPos The voxel coordinates of the voxel to modify
|
||||
* @param weight The weight to set it to
|
||||
* @param value The value to set it to
|
||||
*/
|
||||
public void deformFluidAtLocationToValue(Vector3i worldPos, Vector3i voxelPos, float weight, int value){
|
||||
// TerrainModification modification = new TerrainModification(worldPos,voxelPos,weight,value);
|
||||
// //could be null if, for instance, arena mode
|
||||
// if(model != null){
|
||||
// model.addModification(modification);
|
||||
// }
|
||||
// String key = getKey(worldPos.x,worldPos.y,worldPos.z);
|
||||
// if(chunkCache.containsKey(key)){
|
||||
// ServerFluidChunk chunk = chunkCache.get(key);
|
||||
// chunk.addModification(modification);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a chunk
|
||||
*/
|
||||
public boolean simulate(int worldX, int worldY, int worldZ){
|
||||
if(simulate){
|
||||
ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ);
|
||||
ServerTerrainChunk terrainChunk = this.serverTerrainManager.getChunk(worldX, worldY, worldZ);
|
||||
if(fluidChunk != null && terrainChunk != null && this.serverFluidSimulator != null){
|
||||
return this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//getter for simulate
|
||||
public boolean getSimulate(){
|
||||
return simulate;
|
||||
}
|
||||
|
||||
//setter for simulate
|
||||
public void setSimulate(boolean simulate){
|
||||
this.simulate = simulate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
343
src/main/java/electrosphere/server/fluid/models/FluidModel.java
Normal file
343
src/main/java/electrosphere/server/fluid/models/FluidModel.java
Normal file
@ -0,0 +1,343 @@
|
||||
package electrosphere.server.fluid.models;
|
||||
|
||||
import electrosphere.util.annotation.Exclude;
|
||||
|
||||
public class FluidModel {
|
||||
|
||||
int dynamicInterpolationRatio;
|
||||
float interpolationRandomDampener = 0.4f;
|
||||
|
||||
int discreteArrayDimension;
|
||||
@Exclude
|
||||
private float[][] elevation;
|
||||
|
||||
float realMountainThreshold;
|
||||
float realOceanThreshold;
|
||||
|
||||
FluidModel() {
|
||||
}
|
||||
|
||||
public FluidModel(
|
||||
int dimension,
|
||||
int dynamicInterpolationRatio
|
||||
){
|
||||
|
||||
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
this.discreteArrayDimension = dimension;
|
||||
}
|
||||
|
||||
public static FluidModel constructFluidModel(int dimension, int dynamicInterpolationRatio){
|
||||
FluidModel rVal = new FluidModel();
|
||||
rVal.discreteArrayDimension = dimension;
|
||||
rVal.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public float[][] getElevation(){
|
||||
return elevation;
|
||||
}
|
||||
|
||||
public void setInterpolationRandomDampener(float f){
|
||||
interpolationRandomDampener = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically interpolates a chunk of a specific size from the pre-existing elevation map
|
||||
* @param x The x position on the elevation map to get a chunk from
|
||||
* @param y The y position on the elevation map to get a chunk from
|
||||
* @return Dynamically interpolated float array of elevations of chunk
|
||||
*/
|
||||
public float[][] getElevationForChunk(int x, int y){
|
||||
|
||||
//this is what we intend to return from the function
|
||||
float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio];
|
||||
|
||||
/*
|
||||
So we're looking at chunk x,y
|
||||
|
||||
if this is our grid:
|
||||
|
||||
4 0.1 0.2 0.3 0.4
|
||||
^
|
||||
| 3 0.1 0.2 0.3 0.4
|
||||
|
|
||||
| 2 0.1 0.2 0.3 0.4
|
||||
|
||||
x 1 0.1 0.2 0.3 0.4
|
||||
|
||||
0 1 2 3
|
||||
|
||||
y ---- >
|
||||
|
||||
say we're looking at x=2,y=1
|
||||
|
||||
"macroValues" should contain the values for bounds x = [1,3] and y = [0,2]
|
||||
|
||||
the goal is to have the "center" of the output chunk have the value the
|
||||
elevation grid at x=2,y=1
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//set macroValues
|
||||
float[][] macroValues = getMacroValuesAtPosition(x,y);
|
||||
|
||||
|
||||
|
||||
|
||||
int halfLength = dynamicInterpolationRatio/2;
|
||||
|
||||
/*
|
||||
|
||||
Four quadrants we're generating
|
||||
|
||||
_____________________
|
||||
|1 |2 |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
|__________|__________|
|
||||
|3 |4 |
|
||||
| | |
|
||||
| | |
|
||||
|__________|__________|
|
||||
|
||||
First set of loops is quadrant 1
|
||||
then quadrant 2
|
||||
then quadrant 3
|
||||
then quadrant 4
|
||||
|
||||
*/
|
||||
|
||||
int outXOffset = 0;
|
||||
int outYOffset = 0;
|
||||
|
||||
for(int i = 0; i < halfLength; i++){
|
||||
for(int j = 0; j < halfLength; j++){
|
||||
rVal[i+outXOffset][j+outYOffset] =
|
||||
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] +
|
||||
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] +
|
||||
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][1] +
|
||||
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1]
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
outXOffset = halfLength;
|
||||
for(int i = 0; i < halfLength; i++){
|
||||
for(int j = 0; j < halfLength; j++){
|
||||
rVal[i+outXOffset][j+outYOffset] =
|
||||
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] +
|
||||
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] +
|
||||
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] +
|
||||
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][1]
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
outXOffset = 0;
|
||||
outYOffset = halfLength;
|
||||
for(int i = 0; i < halfLength; i++){
|
||||
for(int j = 0; j < halfLength; j++){
|
||||
rVal[i+outXOffset][j+outYOffset] =
|
||||
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] +
|
||||
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] +
|
||||
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][2] +
|
||||
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2]
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
outXOffset = halfLength;
|
||||
for(int i = 0; i < halfLength; i++){
|
||||
for(int j = 0; j < halfLength; j++){
|
||||
rVal[i+outXOffset][j+outYOffset] =
|
||||
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] +
|
||||
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] +
|
||||
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] +
|
||||
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][2]
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
So we're looking at chunk x,y
|
||||
|
||||
if this is our grid:
|
||||
|
||||
4 0.1 0.2 0.3 0.4
|
||||
^
|
||||
| 3 0.1 0.2 0.3 0.4
|
||||
|
|
||||
| 2 0.1 0.2 0.3 0.4
|
||||
|
||||
x 1 0.1 0.2 0.3 0.4
|
||||
|
||||
0 1 2 3
|
||||
|
||||
y ---- >
|
||||
|
||||
say we're looking at x=2,y=1
|
||||
|
||||
"macroValues" should contain the values for x = [1,3] and y = [0,2]
|
||||
|
||||
the goal is to have the "center" of the output chunk have the value the
|
||||
elevation grid at x=2,y=1
|
||||
|
||||
*/
|
||||
|
||||
public float[][] getMacroValuesAtPosition(int x, int y){
|
||||
|
||||
float[][] rVal = new float[3][3];
|
||||
rVal[1][1] = elevation[x][y];
|
||||
if(x - 1 >= 0){
|
||||
rVal[0][1] = elevation[x-1][y];
|
||||
if(y - 1 >= 0){
|
||||
rVal[0][0] = elevation[x-1][y-1];
|
||||
} else {
|
||||
rVal[0][0] = 0;
|
||||
}
|
||||
if(y + 1 < discreteArrayDimension){
|
||||
rVal[0][2] = elevation[x-1][y+1];
|
||||
} else {
|
||||
rVal[0][2] = 0;
|
||||
}
|
||||
} else {
|
||||
rVal[0][0] = 0;
|
||||
rVal[0][1] = 0;
|
||||
rVal[0][2] = 0;
|
||||
}
|
||||
if(x + 1 < discreteArrayDimension){
|
||||
rVal[2][1] = elevation[x+1][y];
|
||||
if(y - 1 >= 0){
|
||||
rVal[2][0] = elevation[x+1][y-1];
|
||||
} else {
|
||||
rVal[2][0] = 0;
|
||||
}
|
||||
if(y + 1 < discreteArrayDimension){
|
||||
rVal[2][2] = elevation[x+1][y+1];
|
||||
} else {
|
||||
rVal[2][2] = 0;
|
||||
}
|
||||
} else {
|
||||
rVal[2][0] = 0;
|
||||
rVal[2][1] = 0;
|
||||
rVal[2][2] = 0;
|
||||
}
|
||||
if(y - 1 >= 0){
|
||||
rVal[1][0] = elevation[x][y-1];
|
||||
} else {
|
||||
rVal[1][0] = 0;
|
||||
}
|
||||
if(y + 1 < discreteArrayDimension){
|
||||
rVal[1][2] = elevation[x][y+1];
|
||||
} else {
|
||||
rVal[1][2] = 0;
|
||||
}
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public float[][] getRad5MacroValuesAtPosition(int x, int y){
|
||||
|
||||
float[][] rVal = new float[5][5];
|
||||
for(int i = -2; i < 3; i++){
|
||||
for(int j = -2; j < 3; j++){
|
||||
if(x + i >= 0 && x + i < discreteArrayDimension && y + j >= 0 && y + j < discreteArrayDimension){
|
||||
rVal[i+2][j+2] = elevation[x+i][y+j];
|
||||
} else {
|
||||
rVal[i+2][j+2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
So we're looking at chunk x,y
|
||||
|
||||
if this is our grid:
|
||||
|
||||
4 0.1 0.2 0.3 0.4
|
||||
^
|
||||
| 3 0.1 0.2 0.3 0.4
|
||||
|
|
||||
| 2 0.1 0.2 0.3 0.4
|
||||
|
||||
x 1 0.1 0.2 0.3 0.4
|
||||
|
||||
0 1 2 3
|
||||
|
||||
y ---- >
|
||||
|
||||
say we're looking at x=2,y=1
|
||||
|
||||
"macroValues" should contain the values for x = [1,3] and y = [0,2]
|
||||
|
||||
the goal is to have the "center" of the output chunk have the value the
|
||||
elevation grid at x=2,y=1
|
||||
|
||||
*/
|
||||
|
||||
|
||||
public float getRandomDampener(){
|
||||
return interpolationRandomDampener;
|
||||
}
|
||||
|
||||
public int getDynamicInterpolationRatio(){
|
||||
return dynamicInterpolationRatio;
|
||||
}
|
||||
|
||||
public float getRealMountainThreshold() {
|
||||
return realMountainThreshold;
|
||||
}
|
||||
|
||||
public float getRealOceanThreshold() {
|
||||
return realOceanThreshold;
|
||||
}
|
||||
|
||||
public String getModificationKey(int x, int y, int z){
|
||||
return x + "_" + y + "_" + z;
|
||||
}
|
||||
|
||||
// public void addModification(TerrainModification modification){
|
||||
// String key = getModificationKey(modification.getWorldPos().x,modification.getWorldPos().y,modification.getWorldPos().z);
|
||||
// ModificationList list;
|
||||
// if(!modifications.containsKey(key)){
|
||||
// list = new ModificationList();
|
||||
// modifications.put(key, list);
|
||||
// } else {
|
||||
// list = modifications.get(key);
|
||||
// }
|
||||
// list.addModification(modification);
|
||||
// }
|
||||
|
||||
// public boolean containsModificationsAtCoord(int worldX, int worldY, int worldZ){
|
||||
// return modifications.containsKey(getModificationKey(worldX, worldY, worldZ));
|
||||
// }
|
||||
|
||||
// public ModificationList getModifications(int worldX, int worldY, int worldZ){
|
||||
// // System.out.println("Got modifications at " + worldX + " " + worldY);
|
||||
// return modifications.get(getModificationKey(worldX, worldY, worldZ));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sets the elevation array (For instance when read from save file on loading a save)
|
||||
* @param elevation The elevation array to set to
|
||||
*/
|
||||
public void setElevationArray(float[][] elevation){
|
||||
this.elevation = elevation;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package electrosphere.server.fluid.simulator;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* A system capable of simulating a server fluid chunk for a single frame
|
||||
*/
|
||||
public interface ServerFluidSimulator {
|
||||
|
||||
/**
|
||||
* Simulates the chunk for single step
|
||||
* @param fluidChunk The fluid chunk
|
||||
* @param terrainChunk The terrain chunk
|
||||
* @param worldX the world x coordinate
|
||||
* @param worldY the world y coordinate
|
||||
* @param worldZ the world z coordinate
|
||||
* @return True if the server data cell should update all players connected to it with new values, false otherwise
|
||||
*/
|
||||
public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ);
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package electrosphere.server.fluid.simulator.cellularautomata;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.fluid.simulator.ServerFluidSimulator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* Simulates server fluid chunks via cellular automata
|
||||
*/
|
||||
public class FluidCellularAutomataSimulator implements ServerFluidSimulator {
|
||||
|
||||
static final float MAX_WEIGHT = 1.0f;
|
||||
|
||||
static final float GRAVITY_DIFF = 0.04f;
|
||||
|
||||
@Override
|
||||
public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ) {
|
||||
|
||||
float[][][] weights = fluidChunk.getWeights();
|
||||
float[][][] terrainWeights = terrainChunk.getWeights();
|
||||
|
||||
//if true, alerts the server data cell to broadcast a new update message to all clients within it
|
||||
boolean update = false;
|
||||
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
if(weights[x][y][z] <= 0){
|
||||
continue;
|
||||
} else {
|
||||
if(y > 0 && weights[x][y-1][z] < MAX_WEIGHT){
|
||||
update = true;
|
||||
weights[x][y][z] -= GRAVITY_DIFF;
|
||||
weights[x][y-1][z] += GRAVITY_DIFF;
|
||||
} else {
|
||||
//propagate sideways
|
||||
int[] offsetX = new int[]{-1,1,0,0};
|
||||
int[] offsetZ = new int[]{0,0,-1,1};
|
||||
for(int i = 0; i < 4; i++){
|
||||
int realX = x + offsetX[i];
|
||||
int realZ = z + offsetZ[i];
|
||||
if(realX > 0 && realX < ServerTerrainChunk.CHUNK_DIMENSION - 1 &&
|
||||
realZ > 0 && realZ < ServerTerrainChunk.CHUNK_DIMENSION - 1
|
||||
){
|
||||
if(
|
||||
weights[realX][y][realZ] < weights[x][y][z] &&
|
||||
terrainWeights[realX][y][realZ] < MAX_WEIGHT
|
||||
){
|
||||
update = true;
|
||||
weights[x][y][z] -= GRAVITY_DIFF;
|
||||
weights[realX][y][realZ] += GRAVITY_DIFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -27,7 +27,7 @@ public class ArenaChunkGenerator implements ChunkGenerator {
|
||||
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){
|
||||
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){
|
||||
weights[weightX][0][weightZ] = 0.1f;
|
||||
values[weightX][0][weightZ] = 2;
|
||||
values[weightX][0][weightZ] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user