Merge pull request 'Marching Cubes Terrain!' (#134) from marchingCubesTerrain into master
Reviewed-on: https://git.austinwhoover.com/electrosphere/Renderer/pulls/134
5
.gitignore
vendored
@ -36,4 +36,7 @@
|
||||
/netmonitor
|
||||
|
||||
#vscode
|
||||
.settings
|
||||
.settings
|
||||
|
||||
#docs
|
||||
/docs-dist/**
|
||||
8
.vscode/launch.json
vendored
@ -32,6 +32,14 @@
|
||||
"mainClass": "electrosphere.engine.Main",
|
||||
"projectName": "Renderer",
|
||||
"args" : "--headless"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Launch Simulation Only",
|
||||
"request": "launch",
|
||||
"mainClass": "electrosphere.engine.Main",
|
||||
"projectName": "Renderer",
|
||||
"args" : "--simulate"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -136,6 +136,11 @@
|
||||
"isKey": true,
|
||||
"isMouse": false,
|
||||
"keyValue": 89
|
||||
},
|
||||
"placeTerrain": {
|
||||
"isKey": true,
|
||||
"isMouse": false,
|
||||
"keyValue": 52
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,6 @@
|
||||
"graphicsDebugDrawMovementVectors" : false,
|
||||
"graphicsDebugDrawNavmesh" : false,
|
||||
|
||||
"netRunNetMonitor" : true
|
||||
"netRunNetMonitor" : false
|
||||
|
||||
}
|
||||
@ -267,7 +267,7 @@
|
||||
"collidable" : {
|
||||
"type" : "CYLINDER",
|
||||
"dimension1" : 0.1,
|
||||
"dimension2" : 0.45,
|
||||
"dimension2" : 0.9,
|
||||
"dimension3" : 0.1,
|
||||
"offsetX" : 0,
|
||||
"offsetY" : 0.45,
|
||||
|
||||
@ -17,6 +17,71 @@
|
||||
}
|
||||
],
|
||||
"modelPath" : "Models/falloak1.fbx"
|
||||
},
|
||||
{
|
||||
"name" : "Green Grass",
|
||||
"tokens" : [
|
||||
"AMBIENT",
|
||||
"REACTS_TO_WIND",
|
||||
"GROWS_BACK",
|
||||
"FLAMMABLE"
|
||||
],
|
||||
"growthModel": {
|
||||
"growthRate" : 0.001
|
||||
},
|
||||
"modelPath" : "Models/grass1.fbx"
|
||||
},
|
||||
{
|
||||
"name" : "oak",
|
||||
"tokens" : [
|
||||
"TREE",
|
||||
"REACTS_TO_WIND",
|
||||
"GROWS_BACK",
|
||||
"FLAMMABLE"
|
||||
],
|
||||
"growthModel": {
|
||||
"growthRate" : 0.001
|
||||
},
|
||||
"treeModel": {
|
||||
"limbScalarFalloffFactor": 0.3,
|
||||
"minimumLimbScalar": 0.25,
|
||||
"maximumLimbDispersion": 0.5,
|
||||
"minimumLimbDispersion": 0.3,
|
||||
"minimumNumberForks": 3,
|
||||
"maximumNumberForks": 5,
|
||||
"branchHeight": 3,
|
||||
"centralTrunk": false,
|
||||
"maximumTrunkSegments": 4,
|
||||
"maximumBranchSegments": 3,
|
||||
"maxBranchSegmentFalloffFactor": 1,
|
||||
"minimumSegmentToSpawnLeaves": 2,
|
||||
"minBranchHeightToStartSpawningLeaves": 1.5,
|
||||
"maxBranchHeightToStartSpawningLeaves": 3.01,
|
||||
"leafIncrement": 0.5,
|
||||
"minLeavesToSpawnPerPoint": 3,
|
||||
"maxLeavesToSpawnPerPoint": 5,
|
||||
"leafDistanceFromCenter": 1.2,
|
||||
"peelVariance": 0.2,
|
||||
"peelMinimum": 0.1,
|
||||
"swaySigmoidFactor": 2,
|
||||
"minimumSwayTime": 500,
|
||||
"swayTimeVariance": 500,
|
||||
"yawVariance": 0.2,
|
||||
"yawMinimum": 0.1,
|
||||
"minimumScalarToGenerateSwayTree": 0.5,
|
||||
"maximumScalarToGenerateSwayTree": 1.0,
|
||||
"physicsCutoff": 3,
|
||||
"physicsBody": {
|
||||
"type" : "CYLINDER",
|
||||
"dimension1" : 3,
|
||||
"dimension2" : 1,
|
||||
"dimension3" : 1,
|
||||
"offsetX" : 0,
|
||||
"offsetY" : 1.5,
|
||||
"offsetZ" : 0
|
||||
}
|
||||
},
|
||||
"modelPath" : "Models/proceduralTree2/proceduralTree2v2.fbx"
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
19
assets/Data/voxelTypes.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"types" : [
|
||||
{
|
||||
"id" : 0,
|
||||
"name" : "air"
|
||||
},
|
||||
{
|
||||
"id" : 1,
|
||||
"name" : "dirt"
|
||||
},
|
||||
{
|
||||
"id" : 2,
|
||||
"name" : "grass",
|
||||
"ambientFoliage" : [
|
||||
"Green Grass"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/Models/foliageBlockTemplate1Test1.fbx
Normal file
@ -21,6 +21,39 @@
|
||||
"scale" : [0.0015, 0.0015, 0.0015]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path" : "Models/proceduralTree1/proceduralTrunk1.fbx",
|
||||
"meshes" : [
|
||||
{
|
||||
"meshName" : "Cube",
|
||||
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
|
||||
"offset" : [0.0, 0.0, 0.0],
|
||||
"scale" : [1.0, 1.0, 1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path" : "Models/proceduralTree2/proceduralTree2.fbx",
|
||||
"meshes" : [
|
||||
{
|
||||
"meshName" : "Trunk",
|
||||
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
|
||||
"offset" : [0.0, 0.0, 0.0],
|
||||
"scale" : [1.0, 1.0, 1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path" : "Models/proceduralTree2/proceduralTree2v2.fbx",
|
||||
"meshes" : [
|
||||
{
|
||||
"meshName" : "Trunk",
|
||||
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
|
||||
"offset" : [0.0, 0.0, 0.0],
|
||||
"scale" : [1.0, 1.0, 1.0]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/Models/proceduralTree1/proceduralLeafBlob1.fbx
Normal file
BIN
assets/Models/proceduralTree1/proceduralTrunk1.fbx
Normal file
BIN
assets/Models/proceduralTree1/proceduralTrunk1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/Models/proceduralTree2/Trunk.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/Models/proceduralTree2/proceduralTree2.fbx
Normal file
BIN
assets/Models/proceduralTree2/proceduralTree2v2.fbx
Normal file
@ -67,10 +67,8 @@ float calcLightIntensityTotal(vec3 normal);
|
||||
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
|
||||
|
||||
void main(){
|
||||
if(hasTransparency == 1){
|
||||
if(texture(material.diffuse, TexCoord).a < 0.1){
|
||||
discard;
|
||||
}
|
||||
if(texture(material.diffuse, TexCoord).a < 0.01){
|
||||
discard;
|
||||
}
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 viewDir = normalize(viewPos - FragPos);
|
||||
|
||||
208
assets/Shaders/fluid1/fluid1.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 = 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, 1);
|
||||
}
|
||||
|
||||
|
||||
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/fluid1/fluid1.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;
|
||||
}
|
||||
251
assets/Shaders/instanced/colorshift/colorshift.fs
Normal file
@ -0,0 +1,251 @@
|
||||
#version 330 core
|
||||
|
||||
#define NR_POINT_LIGHTS 10
|
||||
|
||||
#define SMALL_EPSILON 0.0001
|
||||
|
||||
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 TexCoord;
|
||||
in vec4 FragPosLightSpace;
|
||||
in vec3 colorShiftValue;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
void main(){
|
||||
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 viewDir = normalize(viewPos - FragPos);
|
||||
|
||||
//grab light intensity
|
||||
float lightIntensity = calcLightIntensityTotal(norm);
|
||||
|
||||
if(texture(material.diffuse, TexCoord).a < SMALL_EPSILON){
|
||||
discard;
|
||||
}
|
||||
|
||||
//get color of base texture
|
||||
vec3 textureColor = texture(material.diffuse, TexCoord).rgb;
|
||||
textureColor = vec3(
|
||||
textureColor.r * (colorShiftValue.x),
|
||||
textureColor.g * (colorShiftValue.y),
|
||||
textureColor.b * (colorShiftValue.z)
|
||||
);
|
||||
|
||||
//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, 1);//texture(ourTexture, TexCoord);//vec4(result, 1.0);
|
||||
}
|
||||
|
||||
// calculates the color when using a directional light.
|
||||
// vec3 CalcDirLight(vec3 normal, vec3 viewDir){
|
||||
// vec3 lightDir = normalize(-dLDirection);
|
||||
// // 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);
|
||||
// // combine results
|
||||
// vec3 texColor = texture(material.diffuse, TexCoord).rgb;
|
||||
// vec3 diffuse = dLDiffuse * diff;
|
||||
// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb);
|
||||
|
||||
|
||||
// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal);
|
||||
|
||||
// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular);
|
||||
// }
|
||||
|
||||
//
|
||||
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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
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;
|
||||
}
|
||||
|
||||
// // calculates the color when using a point light.
|
||||
// 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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
// 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/instanced/colorshift/colorshift.vs
Normal file
@ -0,0 +1,62 @@
|
||||
//Vertex Shader
|
||||
#version 330 core
|
||||
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 2) in vec4 aWeights;
|
||||
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;
|
||||
layout (location = 9) in vec3 colorShift;
|
||||
|
||||
|
||||
//coordinate space transformation matrices
|
||||
uniform mat4 transform;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
|
||||
|
||||
//output buffers
|
||||
out vec3 Normal;
|
||||
out vec3 FragPos;
|
||||
out vec2 TexCoord;
|
||||
out vec4 FragPosLightSpace;
|
||||
out vec3 colorShiftValue;
|
||||
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
mat4 model = mat4(modelA,modelB,modelC,modelD);
|
||||
colorShiftValue = colorShift;
|
||||
|
||||
//normalize posiiton and normal
|
||||
vec4 FinalVertex = vec4(aPos, 1.0);
|
||||
vec4 FinalNormal = vec4(aNormal, 1.0);
|
||||
|
||||
//make sure the W component is 1.0
|
||||
FinalVertex = vec4(FinalVertex.xyz, 1.0);
|
||||
FinalNormal = vec4(FinalNormal.xyz, 1.0);
|
||||
|
||||
|
||||
//push frag, normal, and texture positions to fragment shader
|
||||
FragPos = vec3(model * FinalVertex);
|
||||
Normal = mat3(transpose(inverse(model))) * aNormal;
|
||||
TexCoord = aTex;
|
||||
|
||||
|
||||
//shadow map stuff
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
||||
|
||||
//set final position with opengl space
|
||||
gl_Position = projection * view * model * FinalVertex;
|
||||
}
|
||||
245
assets/Shaders/instanced/generic/generic.fs
Normal file
@ -0,0 +1,245 @@
|
||||
#version 330 core
|
||||
|
||||
#define NR_POINT_LIGHTS 10
|
||||
|
||||
#define SMALL_EPSILON 0.0001
|
||||
|
||||
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 TexCoord;
|
||||
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);
|
||||
|
||||
void main(){
|
||||
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 viewDir = normalize(viewPos - FragPos);
|
||||
|
||||
//grab light intensity
|
||||
float lightIntensity = calcLightIntensityTotal(norm);
|
||||
|
||||
if(texture(material.diffuse, TexCoord).a < SMALL_EPSILON){
|
||||
discard;
|
||||
}
|
||||
|
||||
//get color of base texture
|
||||
vec3 textureColor = 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 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, 1);//texture(ourTexture, TexCoord);//vec4(result, 1.0);
|
||||
}
|
||||
|
||||
// calculates the color when using a directional light.
|
||||
// vec3 CalcDirLight(vec3 normal, vec3 viewDir){
|
||||
// vec3 lightDir = normalize(-dLDirection);
|
||||
// // 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);
|
||||
// // combine results
|
||||
// vec3 texColor = texture(material.diffuse, TexCoord).rgb;
|
||||
// vec3 diffuse = dLDiffuse * diff;
|
||||
// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb);
|
||||
|
||||
|
||||
// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal);
|
||||
|
||||
// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular);
|
||||
// }
|
||||
|
||||
//
|
||||
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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
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;
|
||||
}
|
||||
|
||||
// // calculates the color when using a point light.
|
||||
// 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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
// 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;
|
||||
}
|
||||
60
assets/Shaders/instanced/generic/generic.vs
Normal file
@ -0,0 +1,60 @@
|
||||
//Vertex Shader
|
||||
#version 330 core
|
||||
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 2) in vec4 aWeights;
|
||||
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;
|
||||
|
||||
|
||||
//coordinate space transformation matrices
|
||||
uniform mat4 transform;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
|
||||
|
||||
//output buffers
|
||||
out vec3 Normal;
|
||||
out vec3 FragPos;
|
||||
out vec2 TexCoord;
|
||||
out vec4 FragPosLightSpace;
|
||||
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
mat4 model = mat4(modelA,modelB,modelC,modelD);
|
||||
|
||||
|
||||
//normalize posiiton and normal
|
||||
vec4 FinalVertex = vec4(aPos, 1.0);
|
||||
vec4 FinalNormal = vec4(aNormal, 1.0);
|
||||
|
||||
//make sure the W component is 1.0
|
||||
FinalVertex = vec4(FinalVertex.xyz, 1.0);
|
||||
FinalNormal = vec4(FinalNormal.xyz, 1.0);
|
||||
|
||||
|
||||
//push frag, normal, and texture positions to fragment shader
|
||||
FragPos = vec3(model * FinalVertex);
|
||||
Normal = mat3(transpose(inverse(model))) * aNormal;
|
||||
TexCoord = aTex;
|
||||
|
||||
|
||||
//shadow map stuff
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
||||
|
||||
//set final position with opengl space
|
||||
gl_Position = projection * view * model * FinalVertex;
|
||||
}
|
||||
238
assets/Shaders/instanced/proceduraltree/proceduraltree.fs
Normal file
@ -0,0 +1,238 @@
|
||||
#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 TexCoord;
|
||||
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);
|
||||
|
||||
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 = 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 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);
|
||||
}
|
||||
|
||||
// calculates the color when using a directional light.
|
||||
// vec3 CalcDirLight(vec3 normal, vec3 viewDir){
|
||||
// vec3 lightDir = normalize(-dLDirection);
|
||||
// // 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);
|
||||
// // combine results
|
||||
// vec3 texColor = texture(material.diffuse, TexCoord).rgb;
|
||||
// vec3 diffuse = dLDiffuse * diff;
|
||||
// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb);
|
||||
|
||||
|
||||
// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal);
|
||||
|
||||
// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular);
|
||||
// }
|
||||
|
||||
//
|
||||
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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
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;
|
||||
}
|
||||
|
||||
// // calculates the color when using a point light.
|
||||
// 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];// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
|
||||
// // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
|
||||
// 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;
|
||||
}
|
||||
74
assets/Shaders/instanced/proceduraltree/proceduraltree.vs
Normal file
@ -0,0 +1,74 @@
|
||||
//Vertex Shader
|
||||
#version 330 core
|
||||
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 2) in vec4 aWeights;
|
||||
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;
|
||||
layout (location = 9) in vec4 boneA;
|
||||
layout (location = 10) in vec4 boneB;
|
||||
layout (location = 11) in vec4 boneC;
|
||||
layout (location = 12) in vec4 boneD;
|
||||
layout (location = 13) in float baseSize;
|
||||
|
||||
|
||||
//coordinate space transformation matrices
|
||||
uniform mat4 transform;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
|
||||
|
||||
//output buffers
|
||||
out vec3 Normal;
|
||||
out vec3 FragPos;
|
||||
out vec2 TexCoord;
|
||||
out vec4 FragPosLightSpace;
|
||||
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
mat4 model = mat4(modelA,modelB,modelC,modelD);
|
||||
|
||||
mat4 scaleMatrix = mat4(
|
||||
baseSize,0,0,0,
|
||||
0,baseSize,0,0,
|
||||
0,0,baseSize,0,
|
||||
0,0,0,1
|
||||
);
|
||||
|
||||
mat4 bone = mat4(boneA,boneB,boneC,boneD);
|
||||
mat4 BoneTransform = (bone * aWeights[0]) + (scaleMatrix * (1.0 - aWeights[0]));
|
||||
|
||||
//normalize posiiton and normal
|
||||
vec4 FinalVertex = BoneTransform * vec4(aPos, 1.0);
|
||||
vec4 FinalNormal = BoneTransform * vec4(aNormal, 1.0);
|
||||
|
||||
//make sure the W component is 1.0
|
||||
FinalVertex = vec4(FinalVertex.xyz, 1.0);
|
||||
FinalNormal = vec4(FinalNormal.xyz, 1.0);
|
||||
|
||||
|
||||
//push frag, normal, and texture positions to fragment shader
|
||||
FragPos = vec3(model * FinalVertex);
|
||||
Normal = mat3(transpose(inverse(model))) * aNormal;
|
||||
TexCoord = aTex;
|
||||
|
||||
|
||||
//shadow map stuff
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
||||
|
||||
//set final position with opengl space
|
||||
gl_Position = projection * view * model * FinalVertex;
|
||||
}
|
||||
@ -431,6 +431,32 @@
|
||||
"Textures/skyscraper1.png",
|
||||
"Textures/skyscraper1.png"
|
||||
]
|
||||
},
|
||||
"Models/foliageBlockTemplate1Test1.fbx" : {
|
||||
"Plane" : [
|
||||
"Textures/leaf3layer1.png",
|
||||
"Textures/leaf3layer1.png"
|
||||
],
|
||||
"Plane.001" : [
|
||||
"Textures/leaf3layer1.png",
|
||||
"Textures/leaf3layer1.png"
|
||||
],
|
||||
"Plane.002" : [
|
||||
"Textures/leaf3layer1.png",
|
||||
"Textures/leaf3layer1.png"
|
||||
]
|
||||
},
|
||||
"Models/proceduralTree2/proceduralTree2.fbx": {
|
||||
"Trunk" : [
|
||||
"Models/proceduralTree2/Trunk.png",
|
||||
"Models/proceduralTree2/Trunk.png"
|
||||
]
|
||||
},
|
||||
"Models/proceduralTree2/proceduralTree2v2.fbx": {
|
||||
"Trunk" : [
|
||||
"Models/proceduralTree2/Trunk.png",
|
||||
"Models/proceduralTree2/Trunk.png"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
assets/Textures/leaf3layer1.png
Normal file
|
After Width: | Height: | Size: 183 KiB |
3
buildNumber.properties
Normal file
@ -0,0 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Sat Sep 02 19:48:14 EDT 2023
|
||||
buildNumber=11
|
||||
2864
docs/Doxyfile
Normal file
20
docs/TerrainEditing.txt
Normal file
@ -0,0 +1,20 @@
|
||||
electrosphere.client.terrain.editing.TerrainEditing
|
||||
- Client static interface for editing terrain
|
||||
- The idea is that this provides functions you can call anywhere from client side to trigger a request to perform a terrain edit
|
||||
|
||||
Which leads to
|
||||
|
||||
electrosphere.server.terrain.editing.TerrainEditing
|
||||
- Server utility functions for actually editing terrain
|
||||
- Does the calculations of a real coordinate + radius to determine which cells to edit and how much
|
||||
- This then updates the server terrain manager with edits via the VoxelCellManager interface
|
||||
|
||||
VoxelCellManager interface
|
||||
- Provides an interface on top of DataCellManager to update terrain functions
|
||||
- Makes functions that must be implemented on data cell manager so implementation specific to cell manager
|
||||
- For GriddedDataCellManager, this uses a lock and updates values
|
||||
- As values are updated, they should be send 1-by-1 over the network via individual update packets to the client
|
||||
|
||||
When client receives voxel update packet in ClientTerrainManager, it triggers the cell to update that specific drawcell
|
||||
This should also update all ambient foliage
|
||||
|
||||
13
docs/src/documentation/doxygen.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Documentation System Explanation
|
||||
The engine uses doxygen to document its features, code, and organization.
|
||||
|
||||
## Building
|
||||
On windows, doxywizard can be used to build the documentation in a gui format. Simply open Renderer/docs as the working directory from the top, then open the file "Doxyfile". The project should be preconfigured. Lastly move to the "Run" tab, click "Run doxygen" to generate documentation. You can then optionally click "Show HTML Output" to navigate to the documentation locally via native browser.
|
||||
|
||||
TODO: Linux instructions
|
||||
|
||||
|
||||
## Usage Notes
|
||||
|
||||
### Images
|
||||
Images have proven to be tricky with doxygen on windows. Relative paths only kind of work. Unfortunately this means all images need to be in Renderer/docs/src/images without conflicting names. Potential solutions to look in to are namespacing the file names to the page they show up in.
|
||||
108
docs/src/foliage/treesystem.md
Normal file
@ -0,0 +1,108 @@
|
||||
# Tree System
|
||||
|
||||
|
||||
### The Concept
|
||||
|
||||
The purpose of the tree is to take a branch model and leaf models and construct interesting, complex plants that can be animated and drawn cheaply. An example of a branch might look like the following.
|
||||
|
||||

|
||||
|
||||
|
||||
* The red gismo is the origin point of the entity for the actual model of the branch
|
||||
* The blue arrow is the "branch bone" that controls where the branch model points
|
||||
* The green gizmo is the point that further branches will be placed
|
||||
|
||||
The idea is to attach multiple of these branches together one after another such that the red points of the children coincide with the green points of the parents and furthermore than the rotation of the green points of the parents coincide with the rotation of the red points of the children.
|
||||
|
||||

|
||||
|
||||
### How the branches actually branch
|
||||
|
||||
First the algorithm begins with the vertical line as shown by the purple line in diagram 1. It calculates a "peel" off the vertical line by a certain amount, as shown with the dark blue arc in diagram 2. It then calculates a rotation around the purple vertical as demonstrated by the red arc in diagram 3.
|
||||
|
||||
The branches wouldn't line up if the second rotation was done directly. Instead, the algorithm both rolls and pitches the branch bone to rotate it without any yaw. It then sets the child branch rotations to be the same as this branch bone.
|
||||
|
||||

|
||||
|
||||
### Placing leaves delicately
|
||||
|
||||
The leaf placement algorithm creates sets of rings around the branch bone and places leaf entities evenly spaced along the circles
|
||||
|
||||

|
||||
|
||||
### Wind Behavior Tree
|
||||
|
||||
Todo
|
||||
|
||||
### Explanation of Config Parameters
|
||||
|
||||
`limbScalarFalloffFactor`: how quickly do the limbs shrink
|
||||
|
||||

|
||||
|
||||
`minimumLimbScalar`: How small are the terminal limbs, basically how small can it get before it stops generating
|
||||
|
||||

|
||||
|
||||
`maximumLimbDispersion`: The maximum a single branch can disperse from the current line
|
||||
|
||||
`minimumLimbDispersion`: The minimum a single branch must disperse from the current line
|
||||
|
||||

|
||||
|
||||
`minimumNumberForks`: The minimum number of branch forks per iteration
|
||||
|
||||
`maximumNumberForks`: The maximum number of branch forks per iteration
|
||||
|
||||
`branchHeight`: The height of a single branch, should be the height of the model
|
||||
|
||||
Or, how high is the green above the red
|
||||
|
||||

|
||||
|
||||
`centralTrunk`: if true, always generates a central trunk
|
||||
|
||||
`maximumTrunkSegments`: The maximum number of linear segments for the trunk (ie how many times can a function recurse)
|
||||
|
||||
`maximumBranchSegments`: The maximum number of linear segments for the branch (ie how many times can a function recurse)
|
||||
|
||||
`maxBranchSegmentFalloffFactor`: The rate at which number of branch segments from the current trunk falls off over time
|
||||
|
||||
`minimumSegmentToSpawnLeaves`: The minimum segment number required to start spawning leaves
|
||||
|
||||
`minBranchHeightToStartSpawningLeaves`: the minimum distance along a given segment to start spawning leaves at
|
||||
|
||||
`maxBranchHeightToStartSpawningLeaves`: the maximum distance along a given segment to start spawning leaves at
|
||||
|
||||
`leafIncrement`: The increment along the branch segment to spawn leaves at
|
||||
|
||||
`minLeavesToSpawnPerPoint`: the minimum leaves to spawn per leaf point
|
||||
|
||||
`maxLeavesToSpawnPerPoint`: the maximum leaves to spawn per leaf point
|
||||
|
||||
`leafDistanceFromCenter`: The distance from the central line of a branch to spawn a leaf at
|
||||
|
||||
`peelVariance`: How much can the peel vary hypothetically while it's swinging
|
||||
|
||||
`peelMinimum`: a minimum amount of peel (For instance forcing weather to cause large motions in the branches)
|
||||
|
||||
`swaySigmoidFactor`: The value of the sigmoid controlling branch way speed over time (check branch btree for details)
|
||||
|
||||
`minimumSwayTime`: The minimum number of frames that a branch should sway for
|
||||
|
||||
`swayTimeVariance`: The maximum amount of frames that can be added to minimumSwayTime to increase the time of a single sway
|
||||
|
||||
`yawVariance`: How much can the yaw vary hypothetically while it's swinging
|
||||
|
||||
`yawMinimum`: a minimum amount of yaw (For instance forcing weather to cause large motions in the branches)
|
||||
|
||||
`minimumScalarToGenerateSwayTree`: The minimum scalar of a branch to generate a sway behavior tree
|
||||
|
||||
`maximumScalarToGenerateSwayTree`: The maximum scalar of a branch to generate a sway behavior tree
|
||||
|
||||
|
||||
### Art Direction Ideas
|
||||
|
||||
* High dispersion, low limb count is likely to generate alien plants
|
||||
* High dispersion, high limb count will generate bush-like plants
|
||||
* Low dispersion, high limb count will give good trees
|
||||
BIN
docs/src/images/basicbranch.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
docs/src/images/branchconnectionexample.png
Normal file
|
After Width: | Height: | Size: 322 KiB |
BIN
docs/src/images/branchdispersion.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/src/images/branchfalloff.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/src/images/branchleafplacementconcept.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
docs/src/images/branchminimumscalar.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/src/images/branchpeelexplanation.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
108
docs/src/physics/collision.md
Normal file
@ -0,0 +1,108 @@
|
||||
# Collision Engine
|
||||
|
||||
|
||||
|
||||
|
||||
## High Level Overview
|
||||
The goal of the collision engine system is to allow parallel collision detection of different classes of objects.
|
||||
|
||||
For instance, you could have a collision system dedicated to fire and things that are flammable, where only objects in one of those two categories are present. Or, you could have a collision system dedicated to interactible object prompts, where if the player entity is within one of these zones it performs some prompting logic.
|
||||
|
||||
The big case for this engine is the main physics system. The goal is to provide different classes of things for entities to collide with such that they can control the collision physics differently (think a tree vs the ground vs a slippery floor).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Major Usage Notes
|
||||
|
||||
- All geometries are aligned along z by default in the library (ie your cylinders will be on their sides)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Main Classes
|
||||
|
||||
[CollisionEngine.java](@ref #electrosphere.collision.CollisionEngine) - Represents a specific collision system. It may be helpful to think of it as viewing the world through a specific lens. Keeps track of all entities that do its type of collisions and fires callbacks on collision. Should be updated each tick.
|
||||
|
||||
[Collidable.java](@ref #electrosphere.collision.collidable.Collidable) - Contains the collision information for a single object in a given collision system. Stores both the description of the collidable (is it a tree, a frog, or the ground, etc) as well as a list of impulses to be applied.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Library Explanation
|
||||
|
||||
The library currently in use is Ode4J. There are a couple main classes that will be explained now.
|
||||
|
||||
- DSpace - The main class representing the overall simulation
|
||||
- DWorld - A 'world' within the space that can have geometries inside it that collide. Must be used to create bodies. Probably stores stuff like gravity.
|
||||
- DBody - A rigid body that has some physical properties. Can contain many different geometries.
|
||||
- DGeom - A geometry shape (capsule, box, etc)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Code Organization and Best Practices
|
||||
|
||||
#### Startup
|
||||
Each client scene creates a collision engine for physics on connection. Each scene the server manages also creates a collision engine for physics.
|
||||
|
||||
|
||||
#### Usage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Future Goals
|
||||
|
||||
- Ability to turn off impulse generation for when we purely care about whether things are colliding or not (hitboxes, fire system, ui system, etc)
|
||||
- As always, code organization
|
||||
23
docs/src/physics/physics.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Physics Engine
|
||||
|
||||
## High Level Overview
|
||||
The goal of the physics engine is to wrap around the collision engine to allow physics to occur within the game.
|
||||
|
||||
|
||||
|
||||
|
||||
## Major Usage Notes
|
||||
|
||||
|
||||
|
||||
## Main Classes
|
||||
|
||||
|
||||
|
||||
## Code Organization and Best Practices
|
||||
|
||||
|
||||
|
||||
## Future Goals
|
||||
|
||||
|
||||
346
net/terrain.json
@ -33,6 +33,14 @@
|
||||
"name" : "worldMaxY",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name" : "worldX",
|
||||
"type" : "FIXED_INT"
|
||||
@ -45,27 +53,22 @@
|
||||
"name" : "worldZ",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"name" : "locationX",
|
||||
"name" : "voxelX",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
{
|
||||
"name" : "locationY",
|
||||
"name" : "voxelY",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
{
|
||||
"name" : "locationZ",
|
||||
"name" : "voxelZ",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"name" : "realLocationX",
|
||||
@ -82,215 +85,18 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"name" : "macroValue00",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue01",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue02",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue03",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue04",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue10",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue11",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue12",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue13",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue14",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue20",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue21",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue22",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue23",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue24",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue30",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue31",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue32",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue33",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue34",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue40",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue41",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue42",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue43",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "macroValue44",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
"name" : "randomizerValue00",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue01",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue02",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue03",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue04",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue10",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue11",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue12",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue13",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue14",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue20",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue21",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue22",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue23",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue24",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue30",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue31",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue32",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue33",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue34",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue40",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue41",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue42",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue43",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "randomizerValue44",
|
||||
"type" : "FIXED_LONG"
|
||||
},
|
||||
{
|
||||
"name" : "chunkData",
|
||||
"type" : "BYTE_ARRAY"
|
||||
},
|
||||
|
||||
{
|
||||
"name" : "terrainWeight",
|
||||
"type" : "FIXED_FLOAT"
|
||||
},
|
||||
{
|
||||
"name" : "terrainValue",
|
||||
"type" : "FIXED_INT"
|
||||
}
|
||||
],
|
||||
"messageTypes" : [
|
||||
@ -318,88 +124,40 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "Update",
|
||||
"messageName" : "RequestEditVoxel",
|
||||
"data" : [
|
||||
"locationX",
|
||||
"locationY",
|
||||
"locationZ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "chunkLoadStart",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "MacroValue",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"macroValue00",
|
||||
"macroValue01",
|
||||
"macroValue02",
|
||||
"macroValue03",
|
||||
"macroValue04",
|
||||
"macroValue10",
|
||||
"macroValue11",
|
||||
"macroValue12",
|
||||
"macroValue13",
|
||||
"macroValue14",
|
||||
"macroValue20",
|
||||
"macroValue21",
|
||||
"macroValue22",
|
||||
"macroValue23",
|
||||
"macroValue24",
|
||||
"macroValue30",
|
||||
"macroValue31",
|
||||
"macroValue32",
|
||||
"macroValue33",
|
||||
"macroValue34",
|
||||
"macroValue40",
|
||||
"macroValue41",
|
||||
"macroValue42",
|
||||
"macroValue43",
|
||||
"macroValue44",
|
||||
"randomizerValue00",
|
||||
"randomizerValue01",
|
||||
"randomizerValue02",
|
||||
"randomizerValue03",
|
||||
"randomizerValue04",
|
||||
"randomizerValue10",
|
||||
"randomizerValue11",
|
||||
"randomizerValue12",
|
||||
"randomizerValue13",
|
||||
"randomizerValue14",
|
||||
"randomizerValue20",
|
||||
"randomizerValue21",
|
||||
"randomizerValue22",
|
||||
"randomizerValue23",
|
||||
"randomizerValue24",
|
||||
"randomizerValue30",
|
||||
"randomizerValue31",
|
||||
"randomizerValue32",
|
||||
"randomizerValue33",
|
||||
"randomizerValue34",
|
||||
"randomizerValue40",
|
||||
"randomizerValue41",
|
||||
"randomizerValue42",
|
||||
"randomizerValue43",
|
||||
"randomizerValue44"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "heightMapModification",
|
||||
"data" : [
|
||||
"value",
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"locationX",
|
||||
"locationY",
|
||||
"locationZ"
|
||||
"voxelX",
|
||||
"voxelY",
|
||||
"voxelZ",
|
||||
"terrainWeight",
|
||||
"terrainValue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "UpdateVoxel",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"voxelX",
|
||||
"voxelY",
|
||||
"voxelZ",
|
||||
"terrainWeight",
|
||||
"terrainValue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "RequestUseTerrainPalette",
|
||||
"data" : [
|
||||
"realLocationX",
|
||||
"realLocationY",
|
||||
"realLocationZ",
|
||||
"value",
|
||||
"terrainWeight",
|
||||
"terrainValue"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
91
pom.xml
@ -13,6 +13,14 @@
|
||||
<joml.version>1.9.19</joml.version>
|
||||
</properties>
|
||||
|
||||
<!-- Used for build number plugin because it LITERALLY WONT LET YOU NOT HAVE SCM-->
|
||||
<scm>
|
||||
<connection>scm:svn:http://127.0.0.1/dummy</connection>
|
||||
<developerConnection>scm:svn:https://127.0.0.1/dummy</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<url>http://127.0.0.1/dummy</url>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<!-- generic LWJGL runtimes -->
|
||||
<!--License: BSD-->
|
||||
@ -130,17 +138,17 @@
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
|
||||
<!--jBulletFork-->
|
||||
<!--License: ZLIB-->
|
||||
<!--
|
||||
manual: http://www.cs.kent.edu/~ruttan/GameEngines/lectures/Bullet_User_Manual
|
||||
because their docs ( http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/CylinderShape.html ) suck
|
||||
source: https://github.com/jbullet-maven/jbullet/tree/master/src/main/java/com/bulletphysics
|
||||
-->
|
||||
<!--Ode4J-->
|
||||
<!--License: Dual LGPL 2.1 OR BSD 3-clause-->
|
||||
<!--https://github.com/tzaeschke/ode4j-->
|
||||
<!--https://tzaeschke.github.io/ode4j-old/ode4j-doc.html -->
|
||||
<!--http://ode.org/wikiold/htmlfile1.html -->
|
||||
<!--http://ode.org/wiki/index.php/Main_Page -->
|
||||
<!--https://github.com/tzaeschke/ode4j/tree/master/demo/src/main/java/org/ode4j/demo -->
|
||||
<dependency>
|
||||
<groupId>electrosphere</groupId>
|
||||
<artifactId>jBulletFork</artifactId>
|
||||
<version>0.1</version>
|
||||
<groupId>org.ode4j</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--SQLITE-JDBC-->
|
||||
@ -185,15 +193,6 @@
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--Vecmath explicit import-->
|
||||
<!--License: TBD-->
|
||||
<!--https://mvnrepository.com/artifact/javax.vecmath/vecmath-->
|
||||
<dependency>
|
||||
<groupId>javax.vecmath</groupId>
|
||||
<artifactId>vecmath</artifactId>
|
||||
<version>1.5.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -264,7 +263,19 @@
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
|
||||
<configuration>
|
||||
<executable>java</executable>
|
||||
<includeProjectDependencies>false</includeProjectDependencies>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
<mainClass>electrosphere.engine.Main</mainClass>
|
||||
<!-- <classpathScope>compile</classpathScope> -->
|
||||
<arguments>
|
||||
<argument>-cp</argument>
|
||||
<argument>target/classes;target/Renderer-0.1-jar-with-dependencies.jar</argument>
|
||||
<argument>electrosphere.engine.Main</argument>
|
||||
</arguments>
|
||||
<!-- <classesDirectory>${project.basedir}/target/classes</classesDirectory> -->
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- <execution>
|
||||
<id>Telephone</id>
|
||||
@ -281,28 +292,30 @@
|
||||
</configuration>
|
||||
</execution> -->
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>buildnumber</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>create</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<executable>java</executable>
|
||||
<includeProjectDependencies>false</includeProjectDependencies>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
<mainClass>electrosphere.engine.Main</mainClass>
|
||||
<!-- <classpathScope>compile</classpathScope> -->
|
||||
<arguments>
|
||||
<argument>-cp</argument>
|
||||
<argument>target/classes;target/Renderer-0.1-jar-with-dependencies.jar</argument>
|
||||
<argument>electrosphere.engine.Main</argument>
|
||||
</arguments>
|
||||
<!-- <classesDirectory>${project.basedir}/target/classes</classesDirectory> -->
|
||||
<format>{0,number}</format>
|
||||
<items>
|
||||
<item>buildNumber</item>
|
||||
</items>
|
||||
<doCheck>false</doCheck>
|
||||
<doUpdate>false</doUpdate>
|
||||
<revisionOnScmFailure>unknownbuild</revisionOnScmFailure>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- <plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<argLine>-XstartOnFirstThread</argLine>
|
||||
</configuration>
|
||||
</plugin> -->
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
{"type":"GAME_WORLD","worldMinPoint":{"x":0.0,"y":0.0,"z":0.0},"worldMaxPoint":{"x":200000.0,"y":0.0,"z":200000.0},"worldSizeDiscrete":2000,"dynamicInterpolationRatio":100,"randomDampener":0.0,"isArena":false}
|
||||
@ -43,9 +43,9 @@ public class ClientEntityCullingManager {
|
||||
public void clearOutOfBoundsEntities(){
|
||||
if(Globals.commonWorldData != null && Globals.playerEntity != null && Globals.clientPlayerData != null){
|
||||
Vector3d playerCharacterPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
int playerCharacterWorldX = Globals.commonWorldData.convertRealToWorld(playerCharacterPos.x);
|
||||
int playerCharacterWorldY = Globals.commonWorldData.convertRealToWorld(playerCharacterPos.y);
|
||||
int playerCharacterWorldZ = Globals.commonWorldData.convertRealToWorld(playerCharacterPos.z);
|
||||
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 ||
|
||||
@ -59,22 +59,14 @@ public class ClientEntityCullingManager {
|
||||
//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.commonWorldData.convertRealToWorld(position.x);
|
||||
int worldY = Globals.commonWorldData.convertRealToWorld(position.z);
|
||||
if(!Globals.drawCellManager.coordsInPhysicsSpace(worldX, worldY)){
|
||||
//if we're running the server we need to just hide the entity
|
||||
//otherwise delete it
|
||||
if(Globals.RUN_SERVER && Globals.RUN_CLIENT){
|
||||
recursiveHide(entity);
|
||||
} else {
|
||||
scene.recursiveDeregister(entity);
|
||||
}
|
||||
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, we're running server,
|
||||
//and it's not set to visible, make it visible
|
||||
if(Globals.RUN_SERVER && Globals.RUN_CLIENT){
|
||||
recursiveShow(entity);
|
||||
}
|
||||
//if the entity is within range and it's not set to visible, make it visible
|
||||
recursiveShow(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package electrosphere.client.fluid.cache;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Acts as a cache in front of fluid model to streamline receiving chunks
|
||||
*/
|
||||
public class ClientFluidCache {
|
||||
|
||||
//cache capacity
|
||||
int cacheSize;
|
||||
//the map of chunk key -> chunk data
|
||||
Map<String,FluidChunkData> cacheMap = new ConcurrentHashMap<String,FluidChunkData>();
|
||||
//the list of keys in the cache
|
||||
List<String> cacheList = new CopyOnWriteArrayList<String>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param cacheSize The capacity of the cache
|
||||
*/
|
||||
public ClientFluidCache(int cacheSize){
|
||||
this.cacheSize = cacheSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a chunk data to the fluid cache
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @param chunkData The chunk data to add at the specified positions
|
||||
*/
|
||||
public void addChunkDataToCache(int worldX, int worldY, int worldZ, FluidChunkData chunkData){
|
||||
cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
|
||||
while(cacheList.size() > cacheSize){
|
||||
String currentChunk = cacheList.remove(0);
|
||||
cacheMap.remove(currentChunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a key for the cache based on the position provided
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return The cache key
|
||||
*/
|
||||
public String getKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the cache contains chunk data at a given world point
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return True if the cache contains chunk data at the specified point, false otherwise
|
||||
*/
|
||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return cacheMap.containsKey(getKey(worldX,worldY,worldZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets chunk data at the given world point
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return The chunk data if it exists, null otherwise
|
||||
*/
|
||||
public FluidChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
||||
return cacheMap.get(getKey(worldX,worldY,worldZ));
|
||||
}
|
||||
|
||||
}
|
||||
160
src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
package electrosphere.client.fluid.cache;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* A container of data about a chunk of fluid
|
||||
*/
|
||||
public class FluidChunkData {
|
||||
|
||||
//The size of a chunk in virtual data
|
||||
public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
//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>();
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public float[][][] getVoxelWeight(){
|
||||
return voxelWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the voxel weight array in this container
|
||||
* @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
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public float getWeight(Vector3i localPosition){
|
||||
return voxelWeight[localPosition.x][localPosition.y][localPosition.z];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of a voxel at a position
|
||||
* @param localPosition The local position
|
||||
* @return The type 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for the modifiedSinceLastGeneration set based on a voxel position
|
||||
* @param position The voxel position
|
||||
* @return The key
|
||||
*/
|
||||
private String getVoxelPositionKey(Vector3i position){
|
||||
return position.x + "_" + position.y + "_" + position.z;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
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.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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,414 @@
|
||||
package electrosphere.client.fluid.cells;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.ShaderProgram;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author satellite
|
||||
*/
|
||||
public class FluidDrawCellManager {
|
||||
|
||||
|
||||
//the center of this cell manager's array in cell space
|
||||
int cellX;
|
||||
int cellY;
|
||||
int cellZ;
|
||||
|
||||
|
||||
//the dimensions of the world that this cell manager can handles
|
||||
int cellWidth;
|
||||
|
||||
//the width of a minicell in this manager
|
||||
int miniCellWidth;
|
||||
|
||||
//all currently displaying mini cells
|
||||
Set<FluidDrawCell> cells;
|
||||
Map<String,FluidDrawCell> keyCellMap = new HashMap<String,FluidDrawCell>();
|
||||
Set<String> hasNotRequested;
|
||||
Set<String> hasRequested;
|
||||
Set<String> drawable;
|
||||
Set<String> undrawable;
|
||||
Set<String> updateable;
|
||||
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
|
||||
|
||||
// int drawRadius = 5;
|
||||
int drawStepdownInterval = 3;
|
||||
int drawStepdownValue = 25;
|
||||
|
||||
double drawRadius = 200;
|
||||
|
||||
int physicsRadius = 3;
|
||||
|
||||
int worldBoundDiscreteMin = 0;
|
||||
int worldBoundDiscreteMax = 0;
|
||||
|
||||
//client terrain manager
|
||||
// ClientTerrainManager clientTerrainManager;
|
||||
|
||||
|
||||
//ready to start updating?
|
||||
boolean update = false;
|
||||
|
||||
//controls whether we try to generate the drawable entities
|
||||
//we want this to be false when in server-only mode
|
||||
boolean generateDrawables = false;
|
||||
|
||||
|
||||
/**
|
||||
* DrawCellManager constructor
|
||||
* @param commonWorldData The common world data
|
||||
* @param clientTerrainManager The client terrain manager
|
||||
* @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){
|
||||
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f);
|
||||
cells = new HashSet<FluidDrawCell>();
|
||||
hasNotRequested = new HashSet<String>();
|
||||
drawable = new HashSet<String>();
|
||||
undrawable = new HashSet<String>();
|
||||
updateable = new HashSet<String>();
|
||||
hasRequested = new HashSet<String>();
|
||||
|
||||
cellX = discreteX;
|
||||
cellY = discreteY;
|
||||
cellZ = discreteZ;
|
||||
|
||||
program = Globals.terrainShaderProgram;
|
||||
|
||||
// drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius();
|
||||
drawStepdownInterval = Globals.userSettings.getGameplayPhysicsCellRadius();
|
||||
physicsRadius = Globals.userSettings.getGameplayPhysicsCellRadius();
|
||||
|
||||
invalidateAllCells();
|
||||
|
||||
update = true;
|
||||
}
|
||||
|
||||
FluidDrawCellManager(){
|
||||
|
||||
}
|
||||
|
||||
public void setCell(Vector3i cellPos){
|
||||
cellX = cellPos.x;
|
||||
cellY = cellPos.y;
|
||||
cellZ = cellPos.z;
|
||||
}
|
||||
|
||||
void updateUnrequestedCell(){
|
||||
if(hasNotRequested.size() > 0){
|
||||
String targetKey = hasNotRequested.iterator().next();
|
||||
hasNotRequested.remove(targetKey);
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
// Vector3i vector = getVectorFromKey(targetKey);
|
||||
// int currentCellX = cellX - drawRadius + vector.x;
|
||||
// int currentCellY = cellY - drawRadius + vector.y;
|
||||
// int currentCellZ = cellZ - drawRadius + vector.z;
|
||||
|
||||
if(
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
// if(!hasRequested.contains(targetKey)){
|
||||
//client should request chunk data from server
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
|
||||
worldPos.x,
|
||||
worldPos.y,
|
||||
worldPos.z
|
||||
));
|
||||
undrawable.add(targetKey);
|
||||
hasRequested.add(targetKey);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes one of the undrawable cells drawable
|
||||
*/
|
||||
void makeCellDrawable(){
|
||||
|
||||
if(undrawable.size() > 0){
|
||||
String targetKey = undrawable.iterator().next();
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
if(
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
if(containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){
|
||||
FluidDrawCell cell = FluidDrawCell.generateFluidCell(
|
||||
worldPos,
|
||||
Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z),
|
||||
program
|
||||
);
|
||||
cells.add(cell);
|
||||
keyCellMap.put(targetKey,cell);
|
||||
// undrawable.add(targetKey);
|
||||
undrawable.remove(targetKey);
|
||||
drawable.add(targetKey);
|
||||
//make drawable entity
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
//evaluate for foliage
|
||||
Globals.clientFoliageManager.evaluateChunk(worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a cell that can be updated
|
||||
*/
|
||||
void updateCellModel(){
|
||||
if(updateable.size() > 0){
|
||||
String targetKey = updateable.iterator().next();
|
||||
updateable.remove(targetKey);
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
if(
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
// if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){
|
||||
// needsPhysics[targetX][targetY] = true;
|
||||
// }
|
||||
// int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius);
|
||||
// int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue));
|
||||
// while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){
|
||||
// stride = stride + 1;
|
||||
// }
|
||||
keyCellMap.get(targetKey).destroy();
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
}
|
||||
drawable.add(targetKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean containsUnrequestedCell(){
|
||||
return hasNotRequested.size() > 0;
|
||||
}
|
||||
|
||||
public boolean containsUndrawableCell(){
|
||||
return undrawable.size() > 0;
|
||||
}
|
||||
|
||||
public boolean containsUpdateableCell(){
|
||||
return updateable.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int transformRealSpaceToCellSpace(double input){
|
||||
return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the valid set and adds all keys to invalid set
|
||||
*/
|
||||
public void invalidateAllCells(){
|
||||
drawable.clear();
|
||||
hasNotRequested.clear();
|
||||
clearOutOfBoundsCells();
|
||||
queueNewCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates whether the position of the player has changed and if so, invalidates and cleans up cells accordingly
|
||||
* @param position The position of the player entity on current frame
|
||||
*/
|
||||
public void calculateDeltas(Vector3d position){
|
||||
//check if any not requested cells no longer need to be requested
|
||||
clearOutOfBoundsCells();
|
||||
//check if any cells should be added
|
||||
queueNewCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cells outside of draw radius
|
||||
*/
|
||||
private void clearOutOfBoundsCells(){
|
||||
Set<FluidDrawCell> cellsToRemove = new HashSet<FluidDrawCell>();
|
||||
for(FluidDrawCell cell : cells){
|
||||
Vector3d realPos = cell.getRealPos();
|
||||
if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){
|
||||
cellsToRemove.add(cell);
|
||||
}
|
||||
}
|
||||
for(FluidDrawCell cell : cellsToRemove){
|
||||
cells.remove(cell);
|
||||
String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z);
|
||||
hasNotRequested.remove(key);
|
||||
drawable.remove(key);
|
||||
undrawable.remove(key);
|
||||
updateable.remove(key);
|
||||
keyCellMap.remove(key);
|
||||
hasRequested.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues new cells that are in bounds but not currently accounted for
|
||||
*/
|
||||
private void queueNewCells(){
|
||||
if(Globals.playerEntity != null && Globals.clientWorldData != null){
|
||||
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
for(int x = -(int)drawRadius; x < drawRadius; x = x + ChunkData.CHUNK_SIZE){
|
||||
for(int y = -(int)drawRadius; y < drawRadius; y = y + ChunkData.CHUNK_SIZE){
|
||||
for(int z = -(int)drawRadius; z < drawRadius; z = z + ChunkData.CHUNK_SIZE){
|
||||
Vector3d newPos = new Vector3d(playerPos.x + x, playerPos.y + y, playerPos.z + z);
|
||||
Vector3i worldPos = new Vector3i(
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.x),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.y),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.z)
|
||||
);
|
||||
Vector3d chunkRealSpace = new Vector3d(
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.x),
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.y),
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.z)
|
||||
);
|
||||
if(
|
||||
playerPos.distance(chunkRealSpace) < drawRadius &&
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
String key = getCellKey(
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.x),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.y),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.z)
|
||||
);
|
||||
if(!keyCellMap.containsKey(key) && !hasNotRequested.contains(key) && !undrawable.contains(key) && !drawable.contains(key) &&
|
||||
!hasRequested.contains(key)){
|
||||
hasNotRequested.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates cells that need updating in this manager
|
||||
*/
|
||||
public void update(){
|
||||
if(update){
|
||||
if(containsUnrequestedCell() && !containsUndrawableCell()){
|
||||
updateUnrequestedCell();
|
||||
} else if(containsUndrawableCell()){
|
||||
makeCellDrawable();
|
||||
} else if(containsUpdateableCell()){
|
||||
updateCellModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a cell key into its constituent coordinates in array format.
|
||||
* @param cellKey The cell key to split
|
||||
* @return The coordinates in array format
|
||||
*/
|
||||
// private int[] splitKeyToCoordinates(String cellKey){
|
||||
// int[] rVal = new int[3];
|
||||
// String[] components = cellKey.split("_");
|
||||
// for(int i = 0; i < 3; i++){
|
||||
// rVal[i] = Integer.parseInt(components[i]);
|
||||
// }
|
||||
// return rVal;
|
||||
// }
|
||||
|
||||
public boolean coordsInPhysicsSpace(int worldX, int worldY){
|
||||
return worldX <= cellX + physicsRadius && worldX >= cellX - physicsRadius && worldY <= cellY + physicsRadius && worldY >= cellY - physicsRadius;
|
||||
}
|
||||
|
||||
public void setGenerateDrawables(boolean generate){
|
||||
this.generateDrawables = generate;
|
||||
}
|
||||
|
||||
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk data at a given point
|
||||
* @param worldX The world position x component of the cell
|
||||
* @param worldY The world position y component of the cell
|
||||
* @param worldZ The world position z component of the cell
|
||||
* @return The chunk data at the specified points
|
||||
*/
|
||||
ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
||||
return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a unique key for the cell
|
||||
* @param worldX The world position x component of the cell
|
||||
* @param worldY The world position y component of the cell
|
||||
* @param worldZ The world position z component of the cell
|
||||
* @return The key
|
||||
*/
|
||||
private String getCellKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a vector3i from the cell key
|
||||
* @param key The cell key
|
||||
* @return The vector3i containing the components of the cell key
|
||||
*/
|
||||
private Vector3i getVectorFromKey(String key){
|
||||
String[] keyComponents = key.split("_");
|
||||
return new Vector3i(Integer.parseInt(keyComponents[0]),Integer.parseInt(keyComponents[1]),Integer.parseInt(keyComponents[2]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a data cell as updateable (can be regenerated with a new model because the underlying data has changed)
|
||||
* @param chunkX The chunk x coordinate
|
||||
* @param chunkY The chunk y coordinate
|
||||
* @param chunkZ The chunk z coordinate
|
||||
*/
|
||||
public void markUpdateable(int chunkX, int chunkY, int chunkZ){
|
||||
updateable.add(getCellKey(chunkX, chunkY, chunkZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package electrosphere.client.fluid.editing;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
|
||||
/**
|
||||
* Utilities for editing fluid from client side of things
|
||||
*/
|
||||
public class FluidEditing {
|
||||
|
||||
/**
|
||||
* Performs a fluid chunk edit. Basically has a sphere around the provided position that it attempts to add value to.
|
||||
* @param position The position to perform the edit
|
||||
* @param editMagnitude The magnitude of the edit to perform
|
||||
* @param type The type of block to make all edited blocks
|
||||
* @param weight The weight of the sphere to apply the edit to
|
||||
*/
|
||||
public static void editFluid(Vector3d position, float editMagnitude, int type, float weight){
|
||||
if(position != null){
|
||||
// Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestUseTerrainPaletteMessage(position.x, position.y, position.z, editMagnitude, weight, type));
|
||||
//calculate kernel size
|
||||
// int numPlacesToCheck = (int)((editMagnitude * 2 + 1) * (editMagnitude * 2 + 1) * (editMagnitude * 2 + 1));
|
||||
// //create and fill in kernel of positions to check
|
||||
// int[] xOffsetSet = new int[numPlacesToCheck];
|
||||
// int[] yOffsetSet = new int[numPlacesToCheck];
|
||||
// int[] zOffsetSet = new int[numPlacesToCheck];
|
||||
// int i = 0;
|
||||
// for(int x = -(int)editMagnitude; x <= (int)editMagnitude; x++){
|
||||
// for(int y = -(int)editMagnitude; y <= (int)editMagnitude; y++){
|
||||
// for(int z = -(int)editMagnitude; z <= (int)editMagnitude; z++){
|
||||
// xOffsetSet[i] = x;
|
||||
// yOffsetSet[i] = y;
|
||||
// zOffsetSet[i] = z;
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for(i = 0; i < numPlacesToCheck; i++){
|
||||
// //calculate position of edit
|
||||
// Vector3d offsetPos = new Vector3d(position).add(xOffsetSet[i],yOffsetSet[i],zOffsetSet[i]);
|
||||
// Vector3i chunkPos = Globals.clientWorldData.convertRealToChunkSpace(offsetPos);
|
||||
// Vector3i voxelPos = Globals.clientWorldData.convertRealToVoxelSpace(offsetPos);
|
||||
// //get distance from true center point of sphere to current voxel position in world space
|
||||
// float distance = (float)new Vector3d(Math.floor(offsetPos.x),Math.floor(offsetPos.y),Math.floor(offsetPos.z)).distance(position);
|
||||
// float currentPositionMagnitude = editMagnitude - distance;
|
||||
// ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkPos.x, chunkPos.y, chunkPos.z);
|
||||
// if(
|
||||
// voxelPos.x < ChunkData.CHUNK_SIZE &&
|
||||
// voxelPos.y < ChunkData.CHUNK_SIZE &&
|
||||
// voxelPos.z < ChunkData.CHUNK_SIZE &&
|
||||
// currentPositionMagnitude > 0 &&
|
||||
// data != null
|
||||
// ){
|
||||
// float current = data.getVoxelWeight()[voxelPos.x][voxelPos.y][voxelPos.z];
|
||||
// //hard clamp so it doesn't go over 1
|
||||
// float finalValue = Math.max(Math.min(current + weight,1),-1);
|
||||
// Globals.clientTerrainManager.updateChunk(
|
||||
// chunkPos.x, chunkPos.y, chunkPos.z,
|
||||
// voxelPos.x, voxelPos.y, voxelPos.z,
|
||||
// finalValue, 1
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
package electrosphere.client.fluid.manager;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.fluid.cache.ClientFluidCache;
|
||||
import electrosphere.client.fluid.cache.FluidChunkData;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.fluid.FluidChunkModelData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.meshgen.FluidChunkModelGeneration;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
/**
|
||||
* Manages fluid storage and access on the client
|
||||
*/
|
||||
public class ClientFluidManager {
|
||||
|
||||
//queues messages from server
|
||||
List<TerrainMessage> messageQueue = new CopyOnWriteArrayList<TerrainMessage>();
|
||||
|
||||
//The interpolation ratio of fluid
|
||||
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
||||
|
||||
//caches chunks from server
|
||||
static final int CACHE_SIZE = 50;
|
||||
|
||||
//used for caching the macro values
|
||||
ClientFluidCache fluidCache;
|
||||
|
||||
//The world data for the client
|
||||
ClientWorldData clientWorldData;
|
||||
|
||||
//The queue of fluid chunk data to be buffered to gpu
|
||||
static List<FluidChunkGenQueueItem> fluidChunkGenerationQueue = new CopyOnWriteArrayList<FluidChunkGenQueueItem>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ClientFluidManager(){
|
||||
fluidCache = new ClientFluidCache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
public void handleMessages(){
|
||||
List<TerrainMessage> bouncedMessages = new LinkedList<TerrainMessage>();
|
||||
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];
|
||||
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);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerEngine.WARNING("ClientFluidManager: unhandled network message of type" + message.getMessageSubtype());
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(TerrainMessage message : bouncedMessages){
|
||||
messageQueue.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void attachTerrainMessage(TerrainMessage message){
|
||||
messageQueue.add(message);
|
||||
}
|
||||
|
||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return fluidCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ);
|
||||
}
|
||||
|
||||
public boolean containsChunkDataAtRealPoint(double x, double y, double z){
|
||||
assert clientWorldData != null;
|
||||
return fluidCache.containsChunkDataAtWorldPoint(
|
||||
clientWorldData.convertRealToChunkSpace(x),
|
||||
clientWorldData.convertRealToChunkSpace(y),
|
||||
clientWorldData.convertRealToChunkSpace(z)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk data at a given world position
|
||||
* @param worldX The x component of the world coordinate
|
||||
* @param worldY The y component of the world coordinate
|
||||
* @param worldZ The z component of the world coordinate
|
||||
* @return The chunk data if it exists, otherwise null
|
||||
*/
|
||||
public FluidChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return fluidCache.getSubChunkDataAtPoint(worldX, worldY, worldZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk data at a given world position
|
||||
* @param worldPos The world position as a joml vector
|
||||
* @return The chunk data if it exists, otherwise null
|
||||
*/
|
||||
public FluidChunkData getChunkDataAtWorldPoint(Vector3i worldPos){
|
||||
return fluidCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Queues a fluid chunk to be pushed to GPU based on chunk data
|
||||
* @param data The chunk data (triangles, normals, etc)
|
||||
* @return The model path that is promised to eventually reflect the fluid model when it makes it to gpu
|
||||
*/
|
||||
public static String queueFluidGridGeneration(FluidChunkModelData data){
|
||||
String promisedHash = "";
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
promisedHash = newUUID.toString();
|
||||
FluidChunkGenQueueItem queueItem = new FluidChunkGenQueueItem(data, promisedHash);
|
||||
fluidChunkGenerationQueue.add(queueItem);
|
||||
return promisedHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes all fluid data in queue to the gpu and registers the resulting models
|
||||
*/
|
||||
public static void generateFluidChunkGeometry(){
|
||||
for(FluidChunkGenQueueItem queueItem : fluidChunkGenerationQueue){
|
||||
Model fluidModel = FluidChunkModelGeneration.generateFluidModel(queueItem.getData());
|
||||
Globals.assetManager.registerModelToSpecificString(fluidModel, queueItem.getPromisedHash());
|
||||
}
|
||||
fluidChunkGenerationQueue.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package electrosphere.client.fluid.manager;
|
||||
|
||||
import electrosphere.entity.types.fluid.FluidChunkModelData;
|
||||
|
||||
public class FluidChunkGenQueueItem {
|
||||
|
||||
FluidChunkModelData data;
|
||||
String promisedHash;
|
||||
|
||||
public FluidChunkGenQueueItem(FluidChunkModelData data, String promisedHash){
|
||||
this.data = data;
|
||||
this.promisedHash = promisedHash;
|
||||
}
|
||||
|
||||
public FluidChunkModelData getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getPromisedHash(){
|
||||
return this.promisedHash;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +1,33 @@
|
||||
package electrosphere.client.foliagemanager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityTags;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.camera.CameraEntityUtils;
|
||||
import electrosphere.renderer.actor.instance.InstancedActor;
|
||||
import electrosphere.entity.state.foliage.AmbientFoliage;
|
||||
import electrosphere.game.data.foliage.type.FoliageType;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
|
||||
/**
|
||||
* Manages foliage (grass, small plants, etc) that should be shown, typically instanced
|
||||
* Manages ambient foliage (grass, small plants, etc) that should be shown, typically instanced
|
||||
*/
|
||||
public class ClientFoliageManager {
|
||||
|
||||
@ -35,13 +39,28 @@ public class ClientFoliageManager {
|
||||
|
||||
//Random for finding new positions for foliage
|
||||
Random placementRandomizer = new Random();
|
||||
|
||||
//The list of grass entities currently in use
|
||||
Set<Entity> grassEntities = new HashSet<Entity>();
|
||||
|
||||
//Used to prevent concurrent usage of grassEntities set
|
||||
boolean ready = false;
|
||||
|
||||
//The list of voxel type ids that should have grass generated on top of them
|
||||
static final List<Integer> grassGeneratingVoxelIds = new ArrayList<Integer>();
|
||||
|
||||
//FoliageCells that are active and have foliage that is being drawn
|
||||
Set<FoliageCell> activeCells = new HashSet<FoliageCell>();
|
||||
//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;
|
||||
//The maximum number of foliage cells
|
||||
static final int CELL_COUNT_MAX = 100;
|
||||
//The target number of foliage to place per cell
|
||||
static final int TARGET_FOLIAGE_PER_CELL = 50;
|
||||
//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>();
|
||||
//The number of frames that must pass before a cell can be reevaluated for foliage placement
|
||||
static final int EVALUATION_COOLDOWN = 100;
|
||||
|
||||
|
||||
|
||||
@ -59,6 +78,9 @@ public class ClientFoliageManager {
|
||||
};
|
||||
modelMatrixAttribute = new ShaderAttribute(attributeIndices);
|
||||
attributes.put(modelMatrixAttribute,HomogenousBufferTypes.MAT4F);
|
||||
|
||||
//set grass generating voxel ids
|
||||
grassGeneratingVoxelIds.add(2);
|
||||
}
|
||||
|
||||
//shader paths
|
||||
@ -80,24 +102,11 @@ public class ClientFoliageManager {
|
||||
* Starts up the foliage manager
|
||||
*/
|
||||
public void start(){
|
||||
//queue grass model
|
||||
Globals.assetManager.addModelPathToQueue("models/grass1.fbx");
|
||||
|
||||
Vector3d centerPosition = new Vector3d(0,0,0);
|
||||
//create grass entities
|
||||
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
|
||||
makeEntityInstancedFoliage(grassEntity, "models/grass1.fbx", grassCapacity);
|
||||
EntityUtils.getPosition(grassEntity).set(getNewPosition(centerPosition));
|
||||
EntityUtils.getRotation(grassEntity).set(getNewRotation());
|
||||
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
|
||||
grassEntities.add(grassEntity);
|
||||
for(int i = 0; i < grassCapacity - 1; i++){
|
||||
grassEntity = EntityCreationUtils.createClientSpatialEntity();
|
||||
makeEntityInstancedFoliage(grassEntity, "models/grass1.fbx", grassCapacity);
|
||||
EntityUtils.getPosition(grassEntity).set(getNewPosition(centerPosition));
|
||||
EntityUtils.getRotation(grassEntity).set(getNewRotation());
|
||||
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
|
||||
grassEntities.add(grassEntity);
|
||||
//queue ambient foliage models
|
||||
for(FoliageType foliageType : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){
|
||||
if(foliageType.getTokens().contains(FoliageType.TOKEN_AMBIENT)){
|
||||
Globals.assetManager.addModelPathToQueue(foliageType.getModelPath());
|
||||
}
|
||||
}
|
||||
ready = true;
|
||||
}
|
||||
@ -107,38 +116,35 @@ public class ClientFoliageManager {
|
||||
*/
|
||||
public void update(){
|
||||
if(ready){
|
||||
Matrix4f modelMatrix = new Matrix4f();
|
||||
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
for(Entity grassEntity : grassEntities){
|
||||
Vector3d grassPosition = EntityUtils.getPosition(grassEntity);
|
||||
Quaternionf grassRotation = EntityUtils.getRotation(grassEntity);
|
||||
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity);
|
||||
InstancedActor instancedActor = InstancedActor.getInstancedActor(grassEntity);
|
||||
//update position
|
||||
if(playerPosition.distance(grassPosition) > GRASS_RELOCATION_THRESHOLD){
|
||||
grassPosition.set(getNewPosition(playerPosition));
|
||||
grassRotation.set(getNewRotation());
|
||||
}
|
||||
//update uniforms
|
||||
// instancedActor.setUniform("position", grassPosition);
|
||||
// instancedActor.setUniform("rotation", new Vector4d(
|
||||
// grassRotation.x,
|
||||
// grassRotation.y,
|
||||
// grassRotation.z,
|
||||
// grassRotation.w));
|
||||
// instancedActor.setUniform("scale", new Vector3d(EntityUtils.getScale(grassEntity)));
|
||||
|
||||
modelMatrix = modelMatrix.identity();
|
||||
Vector3f cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
|
||||
modelMatrix.translate(cameraModifiedPosition);
|
||||
modelMatrix.rotate(grassRotation);
|
||||
modelMatrix.scale(EntityUtils.getScale(grassEntity));
|
||||
|
||||
instancedActor.setAttribute(modelMatrixAttribute, modelMatrix);
|
||||
|
||||
//draw
|
||||
instancedActor.draw(Globals.renderingEngine.getRenderPipelineState());
|
||||
//TODO: frustum cull at cell level before individual model level
|
||||
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);
|
||||
cooldownTime--;
|
||||
if(cooldownTime <= 0){
|
||||
String split[] = key.split("_");
|
||||
Vector3i worldPos = new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2]));
|
||||
Vector3i voxelPos = new Vector3i(Integer.parseInt(split[3]),Integer.parseInt(split[4]),Integer.parseInt(split[5]));
|
||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
//evaluate
|
||||
if(
|
||||
data.getWeight(voxelPos) > 0 &&
|
||||
data.getWeight(new Vector3i(voxelPos.x,voxelPos.y + 1,voxelPos.z)) < 0 &&
|
||||
typeSupportsFoliage(data.getType(voxelPos))
|
||||
){
|
||||
//create foliage cell
|
||||
createFoliageCell(worldPos,voxelPos,0);
|
||||
}
|
||||
locationEvaluationCooldownMap.remove(key);
|
||||
} else {
|
||||
locationEvaluationCooldownMap.put(key, cooldownTime);
|
||||
}
|
||||
}
|
||||
//invalidate foliage cells that have had their voxel changed
|
||||
invalidateModifiedPositions();
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,34 +167,207 @@ public class ClientFoliageManager {
|
||||
* Gets a new rotation for a blade of grass
|
||||
* @return The rotation
|
||||
*/
|
||||
protected Quaternionf getNewRotation(){
|
||||
return new Quaternionf().rotationX(-(float)Math.PI / 2.0f).rotateLocalY((float)Math.PI * placementRandomizer.nextFloat());
|
||||
protected Quaterniond getNewRotation(){
|
||||
return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets a key for a foliage cell in the localCellMap
|
||||
* @param worldPosition The world position of the cell
|
||||
* @param voxelPosition The voxel position of the cell
|
||||
* @return The key for the cell
|
||||
*/
|
||||
private String getFoliageCellKey(Vector3i worldPosition, Vector3i voxelPosition){
|
||||
return worldPosition.x + "_" + worldPosition.y + "_" + worldPosition.z + "_" + voxelPosition.x + "_" + voxelPosition.y + "_" + voxelPosition.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor
|
||||
* @param entity The entity
|
||||
* @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){
|
||||
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 Quaternionf().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
Globals.clientScene.registerEntity(entity);
|
||||
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED);
|
||||
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED_MANAGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a chunk to see where foliage cells should be created or updated
|
||||
* @param worldPos The world position of the chunk
|
||||
*/
|
||||
public void evaluateChunk(Vector3i worldPos){
|
||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
|
||||
//can't go to very top 'cause otherwise there would be no room to put grass
|
||||
for(int y = 0; y < ChunkData.CHUNK_SIZE - 1; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
|
||||
Vector3i currentPos = new Vector3i(x,y,z);
|
||||
String key = getFoliageCellKey(worldPos, currentPos);
|
||||
if(locationCellMap.get(key) != null){
|
||||
//destroy if there's no longer ground or
|
||||
//if the cell above is now occupied or
|
||||
//if the lower cell is no longer supporting foliage
|
||||
if(
|
||||
data.getWeight(currentPos) <= 0 ||
|
||||
data.getWeight(new Vector3i(x,y + 1,z)) > 0 ||
|
||||
!typeSupportsFoliage(data.getType(currentPos))
|
||||
){
|
||||
//destroy
|
||||
FoliageCell toDestroy = locationCellMap.get(key);
|
||||
toDestroy.destroy();
|
||||
activeCells.remove(toDestroy);
|
||||
locationCellMap.remove(key);
|
||||
} else {
|
||||
//TODO: evaluate if foliage is placed well
|
||||
}
|
||||
} else {
|
||||
//create if current is ground and above is air
|
||||
if(
|
||||
!locationEvaluationCooldownMap.containsKey(key) &&
|
||||
data.getWeight(currentPos) > 0 &&
|
||||
data.getWeight(new Vector3i(x,y + 1,z)) < 0 &&
|
||||
typeSupportsFoliage(data.getType(currentPos))
|
||||
){
|
||||
//create foliage cell
|
||||
createFoliageCell(worldPos,currentPos,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//evaluate top cells if chunk above this one exists
|
||||
ChunkData aboveData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
|
||||
if(aboveData != null){
|
||||
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
|
||||
Vector3i currentPos = new Vector3i(x,ChunkData.CHUNK_SIZE-1,z);
|
||||
String key = getFoliageCellKey(worldPos, currentPos);
|
||||
if(locationCellMap.get(key) != null){
|
||||
//destroy if there's no longer ground or
|
||||
//if the cell above is now occupied or
|
||||
//if the lower cell is no longer supporting foliage
|
||||
if(
|
||||
data.getWeight(currentPos) <= 0 ||
|
||||
aboveData.getWeight(new Vector3i(x,0,z)) > 0 ||
|
||||
!typeSupportsFoliage(data.getType(currentPos))
|
||||
){
|
||||
//destroy
|
||||
FoliageCell toDestroy = locationCellMap.get(key);
|
||||
toDestroy.destroy();
|
||||
activeCells.remove(toDestroy);
|
||||
locationCellMap.remove(key);
|
||||
} else {
|
||||
//TODO: evaluate if foliage is placed well
|
||||
}
|
||||
} else {
|
||||
//create if current is ground and above is air
|
||||
if(
|
||||
data.getWeight(currentPos) > 0 &&
|
||||
aboveData.getWeight(new Vector3i(x,0,z)) < 0 &&
|
||||
typeSupportsFoliage(data.getType(currentPos))
|
||||
){
|
||||
//create foliage cell
|
||||
createFoliageCell(worldPos,currentPos,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a foliage cell at a given position
|
||||
* @param worldPos The world position
|
||||
* @param voxelPos The voxel position
|
||||
*/
|
||||
private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel){
|
||||
//get foliage types supported
|
||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPos)).getAmbientFoliage();
|
||||
if(foliageTypesSupported != null){
|
||||
FoliageCell cell = new FoliageCell(worldPos, voxelPos);
|
||||
//create center foliage
|
||||
for(int i = 0; i < TARGET_FOLIAGE_PER_CELL; i++){
|
||||
//get type
|
||||
String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size());
|
||||
FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName);
|
||||
//get position to place
|
||||
double offsetX = placementRandomizer.nextDouble() * 0.6 + 0.2;
|
||||
double offsetZ = placementRandomizer.nextDouble() * 0.6 + 0.2;
|
||||
Vector3d absolutePosition = new Vector3d(
|
||||
worldPos.x * ChunkData.CHUNK_SIZE + voxelPos.x + offsetX,
|
||||
worldPos.y * ChunkData.CHUNK_SIZE + voxelPos.y + data.getWeight(voxelPos) * 0.6,
|
||||
worldPos.z * ChunkData.CHUNK_SIZE + voxelPos.z + offsetZ
|
||||
);
|
||||
//create entity
|
||||
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
|
||||
makeEntityInstancedFoliage(grassEntity, foliageType.getModelPath(), grassCapacity);
|
||||
EntityUtils.getPosition(grassEntity).set(absolutePosition);
|
||||
EntityUtils.getRotation(grassEntity).set(getNewRotation());
|
||||
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
|
||||
//add ambient foliage behavior tree
|
||||
AmbientFoliage.attachAmbientFoliageTree(grassEntity, initialGrowthLevel, foliageType.getGrowthModel().getGrowthRate());
|
||||
cell.addEntity(grassEntity);
|
||||
}
|
||||
activeCells.add(cell);
|
||||
locationCellMap.put(getFoliageCellKey(worldPos, voxelPos),cell);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the voxel type supports foliage or not
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private boolean typeSupportsFoliage(int type){
|
||||
if(Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type) != null){
|
||||
return Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type).getAmbientFoliage() != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates a foliage cell at a position, destroying all foliage in the cell and unregistering it.
|
||||
* Furthermore, it adds it to a cooldown queue to wait until it can recreate foliage
|
||||
* @param worldPosition The world position of the cell
|
||||
* @param voxelPosition The voxel position of the cell
|
||||
*/
|
||||
private void invalidateCell(Vector3i worldPosition, Vector3i voxelPosition){
|
||||
String key = getFoliageCellKey(worldPosition, voxelPosition);
|
||||
if(!locationEvaluationCooldownMap.containsKey(key)){
|
||||
locationEvaluationCooldownMap.put(key,EVALUATION_COOLDOWN);
|
||||
FoliageCell cell = locationCellMap.get(key);
|
||||
if(cell != null){
|
||||
//destroy
|
||||
FoliageCell toDestroy = locationCellMap.get(key);
|
||||
toDestroy.destroy();
|
||||
activeCells.remove(toDestroy);
|
||||
locationCellMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the foliage cells for all modified chunks
|
||||
*/
|
||||
private void invalidateModifiedPositions(){
|
||||
for(ChunkData chunk : Globals.clientTerrainManager.getAllChunks()){
|
||||
if(chunk.getModifiedPositions().size() > 0){
|
||||
for(Vector3i position : chunk.getModifiedPositions()){
|
||||
Globals.clientFoliageManager.invalidateCell(Globals.clientTerrainManager.getPositionOfChunk(chunk), position);
|
||||
}
|
||||
chunk.resetModifiedPositions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
package electrosphere.client.foliagemanager;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.state.foliage.AmbientFoliage;
|
||||
import electrosphere.entity.types.camera.CameraEntityUtils;
|
||||
import electrosphere.renderer.actor.instance.InstancedActor;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
|
||||
/**
|
||||
* Contains a set of foliage entities and groups them together.
|
||||
*/
|
||||
public class FoliageCell {
|
||||
//position of the foliage cell in world coordinates
|
||||
protected Vector3i worldPosition;
|
||||
//position of the foliage cell in local coordinates
|
||||
protected Vector3i voxelPosition;
|
||||
//constituent entities
|
||||
protected Set<Entity> containedEntities;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param worldPos The position of the foliage cell in world coordinates
|
||||
* @param voxelPos The position of the foliage cell in voxel coordinates
|
||||
*/
|
||||
protected FoliageCell(Vector3i worldPos, Vector3i voxelPos){
|
||||
this.worldPosition = worldPos;
|
||||
this.voxelPosition = voxelPos;
|
||||
this.containedEntities = new HashSet<Entity>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity to this foliage cell
|
||||
* @param entity The entity to add
|
||||
*/
|
||||
protected void addEntity(Entity entity){
|
||||
containedEntities.add(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all entities in this foliage cell
|
||||
*/
|
||||
protected void clearEntities(){
|
||||
containedEntities.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all entities in this foliage cell
|
||||
*/
|
||||
protected void destroy(){
|
||||
for(Entity entity : containedEntities){
|
||||
EntityUtils.cleanUpEntity(entity);
|
||||
}
|
||||
clearEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all entities in the foliage cell
|
||||
* @param modelMatrixAttribute The model matrix attribute to draw with
|
||||
*/
|
||||
protected void draw(ShaderAttribute modelMatrixAttribute){
|
||||
Matrix4d modelMatrix = new Matrix4d();
|
||||
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
|
||||
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity);
|
||||
for(Entity entity : containedEntities){
|
||||
Vector3d grassPosition = EntityUtils.getPosition(entity);
|
||||
Quaterniond grassRotation = EntityUtils.getRotation(entity);
|
||||
InstancedActor instancedActor = InstancedActor.getInstancedActor(entity);
|
||||
|
||||
modelMatrix = modelMatrix.identity();
|
||||
Vector3f 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)));
|
||||
|
||||
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f(modelMatrix));
|
||||
|
||||
//set priority equal to distance
|
||||
instancedActor.setPriority((int)grassPosition.distance(playerPosition));
|
||||
|
||||
//draw
|
||||
instancedActor.draw(Globals.renderingEngine.getRenderPipelineState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,9 @@ package electrosphere.client.scene;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.Scene;
|
||||
import electrosphere.game.collision.CollisionEngine;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
|
||||
/**
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
package electrosphere.client.scene;
|
||||
|
||||
import electrosphere.game.server.world.*;
|
||||
import electrosphere.server.datacell.ServerDataCell;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
import java.util.List;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -44,7 +40,13 @@ public class ClientWorldData {
|
||||
int worldDiscreteSize;
|
||||
|
||||
|
||||
public ClientWorldData(Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize) {
|
||||
public ClientWorldData(
|
||||
Vector3f worldMinPoint,
|
||||
Vector3f worldMaxPoint,
|
||||
int dynamicInterpolationRatio,
|
||||
float randomDampener,
|
||||
int worldDiscreteSize
|
||||
) {
|
||||
this.worldMinPoint = worldMinPoint;
|
||||
this.worldMaxPoint = worldMaxPoint;
|
||||
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
@ -84,4 +86,41 @@ public class ClientWorldData {
|
||||
return chunk * dynamicInterpolationRatio;
|
||||
}
|
||||
|
||||
public int convertRealToWorld(double real){
|
||||
return convertRealToChunkSpace(real);
|
||||
}
|
||||
|
||||
public double convertWorldToReal(int world){
|
||||
return convertChunkToRealSpace(world);
|
||||
}
|
||||
|
||||
public Vector3i convertRealToChunkSpace(Vector3d position){
|
||||
return new Vector3i(
|
||||
convertRealToChunkSpace(position.x),
|
||||
convertRealToChunkSpace(position.y),
|
||||
convertRealToChunkSpace(position.z)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a world space vector to a real space vector
|
||||
* @param position The world space vector
|
||||
* @return The real space vector
|
||||
*/
|
||||
public Vector3d convertWorldToRealSpace(Vector3i position){
|
||||
return new Vector3d(
|
||||
convertWorldToReal(position.x),
|
||||
convertWorldToReal(position.y),
|
||||
convertWorldToReal(position.z)
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3i convertRealToVoxelSpace(Vector3d position){
|
||||
return new Vector3i(
|
||||
(int)Math.floor(position.x - convertChunkToRealSpace(convertRealToChunkSpace(position.x))),
|
||||
(int)Math.floor(position.y - convertChunkToRealSpace(convertRealToChunkSpace(position.y))),
|
||||
(int)Math.floor(position.z - convertChunkToRealSpace(convertRealToChunkSpace(position.z)))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,10 +3,8 @@ package electrosphere.client.sim;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.camera.CameraEntityUtils;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -45,7 +43,6 @@ public class ClientFunctions {
|
||||
public static void loadTerrain(){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
Globals.clientTerrainManager.handleMessages();
|
||||
Globals.clientTerrainManager.ejectLoadedChunks();
|
||||
updateCellManager();
|
||||
}
|
||||
}
|
||||
@ -59,7 +56,7 @@ public class ClientFunctions {
|
||||
newPlayerCharacterPosition = EntityUtils.getPosition(Globals.playerEntity);
|
||||
}
|
||||
//Cell manager do your things
|
||||
Globals.drawCellManager.calculateDeltas(oldPlayerCharacterPosition, newPlayerCharacterPosition);
|
||||
Globals.drawCellManager.calculateDeltas(newPlayerCharacterPosition);
|
||||
Globals.drawCellManager.update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,9 +57,7 @@ public class ClientSimulation {
|
||||
}
|
||||
}
|
||||
//update foliage
|
||||
if(Globals.clientFoliageManager != null){
|
||||
Globals.clientFoliageManager.update();
|
||||
}
|
||||
Globals.clientFoliageManager.update();
|
||||
//tally collidables and offset position accordingly
|
||||
// for(Entity currentCollidable : Globals.entityManager.getEntitiesWithTag(EntityTags.COLLIDABLE)){
|
||||
// CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable);
|
||||
|
||||
182
src/main/java/electrosphere/client/terrain/cache/ChunkData.java
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
package electrosphere.client.terrain.cache;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* A container of data about a chunk of terrain
|
||||
*/
|
||||
public class ChunkData {
|
||||
|
||||
//The size of a chunk in virtual data
|
||||
public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
//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 terrain is in this voxel, eg stone vs dirt vs grass, etc
|
||||
int[][][] voxelType;
|
||||
//How much of that terrain 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>();
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public float[][][] getVoxelWeight(){
|
||||
return voxelWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the voxel weight array in this container
|
||||
* @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
|
||||
* @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 localX The x coordinate
|
||||
* @param localY The y coordinate
|
||||
* @param localZ The z coordinate
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public float getWeight(int localX, int localY, int localZ){
|
||||
return voxelWeight[localX][localY][localZ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of a voxel at a position
|
||||
* @param localPosition The local position
|
||||
* @return The type of the specified voxel
|
||||
*/
|
||||
public int getType(Vector3i localPosition){
|
||||
return getType(localPosition.x,localPosition.y,localPosition.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of a voxel at a position
|
||||
* @param localX The x coordinate
|
||||
* @param localY The y coordinate
|
||||
* @param localZ The z coordinate
|
||||
* @return The type of the specified voxel
|
||||
*/
|
||||
public int getType(int localX, int localY, int localZ){
|
||||
return voxelType[localX][localY][localZ];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for the modifiedSinceLastGeneration set based on a voxel position
|
||||
* @param position The voxel position
|
||||
* @return The key
|
||||
*/
|
||||
private String getVoxelPositionKey(Vector3i position){
|
||||
return position.x + "_" + position.y + "_" + position.z;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package electrosphere.client.terrain.cache;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class ChunkModification {
|
||||
int worldX;
|
||||
int worldY;
|
||||
int locationX;
|
||||
int locationY;
|
||||
float value;
|
||||
|
||||
public ChunkModification(int worldX, int worldY, int locationX, int locationY, float value) {
|
||||
this.worldX = worldX;
|
||||
this.worldY = worldY;
|
||||
this.locationX = locationX;
|
||||
this.locationY = locationY;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getWorldX() {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
public int getLocationX() {
|
||||
return locationX;
|
||||
}
|
||||
|
||||
public int getLocationY() {
|
||||
return locationY;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getWorldY() {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
public float[][] applyModification(float[][] heightmap){
|
||||
heightmap[locationX][locationY] = value;
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,73 +1,46 @@
|
||||
package electrosphere.client.terrain.cache;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.game.terrain.processing.TerrainInterpolator;
|
||||
import electrosphere.server.terrain.models.TerrainModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
|
||||
/**
|
||||
* Acts as a cache in front of terrain model to streamline receiving chunks
|
||||
*/
|
||||
public class ClientTerrainCache {
|
||||
|
||||
/*
|
||||
Acts as a cache in front of terrain model to streamline receiving heightmaps
|
||||
on both client and server
|
||||
*/
|
||||
|
||||
|
||||
|
||||
ClientWorldData clientWorldData;
|
||||
|
||||
|
||||
//cache capacity
|
||||
int cacheSize;
|
||||
Map<String,float[][]> cacheMap = new ConcurrentHashMap<String,float[][]> ();
|
||||
//the map of chunk key -> chunk data
|
||||
Map<String,ChunkData> cacheMap = new ConcurrentHashMap<String,ChunkData>();
|
||||
//the list of keys in the cache
|
||||
List<String> cacheList = new CopyOnWriteArrayList<String>();
|
||||
//A map of chunk to its world position
|
||||
Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param cacheSize The capacity of the cache
|
||||
*/
|
||||
public ClientTerrainCache(int cacheSize){
|
||||
this.cacheSize = cacheSize;
|
||||
}
|
||||
|
||||
// TerrainChunkValues getChunkValuesAtPoint(int x, int y){
|
||||
// if(terrainModel != null){
|
||||
// return new TerrainChunkValues(
|
||||
// terrainModel.getMacroValuesAtPosition(x, y),
|
||||
// terrainModel.getRandomizerValuesAtPosition(x, y)
|
||||
// );
|
||||
// } else {
|
||||
// return new TerrainChunkValues(
|
||||
// new float[3][3],
|
||||
// new long[3][3]
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
//USED TO ADD macro values
|
||||
public void addChunkValuesToCache(int x, int y, float[][] macroValues, long[][] randomizer){
|
||||
// TerrainChunkValues chunk = new TerrainChunkValues(macroValues, randomizer);
|
||||
assert clientWorldData != null;
|
||||
|
||||
float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk(
|
||||
macroValues,
|
||||
randomizer,
|
||||
clientWorldData.getDynamicInterpolationRatio(),
|
||||
clientWorldData.getRandomDampener()
|
||||
);
|
||||
|
||||
cacheMap.put(getKey(x,y),heightmap);
|
||||
while(cacheList.size() > cacheSize){
|
||||
String currentChunk = cacheList.remove(0);
|
||||
cacheMap.remove(currentChunk);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFloatsToCache(int x, int y, float[][] values){
|
||||
cacheMap.put(getKey(x,y),values);
|
||||
/**
|
||||
* Adds a chunk data to the terrain cache
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @param chunkData The chunk data to add at the specified positions
|
||||
*/
|
||||
public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){
|
||||
cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
|
||||
chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
|
||||
while(cacheList.size() > cacheSize){
|
||||
String currentChunk = cacheList.remove(0);
|
||||
cacheMap.remove(currentChunk);
|
||||
@ -75,29 +48,58 @@ public class ClientTerrainCache {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getKey(int x, int y){
|
||||
return x + "-" + y;
|
||||
/**
|
||||
* Generates a key for the cache based on the position provided
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return The cache key
|
||||
*/
|
||||
public String getKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
}
|
||||
|
||||
|
||||
public boolean containsHeightmapAtChunkPoint(int x, int y){
|
||||
return cacheMap.containsKey(getKey(x,y));
|
||||
/**
|
||||
* Checks whether the cache contains chunk data at a given world point
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return True if the cache contains chunk data at the specified point, false otherwise
|
||||
*/
|
||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return cacheMap.containsKey(getKey(worldX,worldY,worldZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//ALL RETRIEVE
|
||||
public float[][] getHeightmapAtChunkPoint(int x, int y){
|
||||
return cacheMap.get(getKey(x,y));
|
||||
/**
|
||||
* Gets chunk data at the given world point
|
||||
* @param worldX The x world position
|
||||
* @param worldY The y world position
|
||||
* @param worldZ The z world position
|
||||
* @return The chunk data if it exists, null otherwise
|
||||
*/
|
||||
public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
||||
return cacheMap.get(getKey(worldX,worldY,worldZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setClientWorldData(ClientWorldData clientWorldData){
|
||||
this.clientWorldData = clientWorldData;
|
||||
/**
|
||||
* Gets the list of all chunks in the cache
|
||||
* @return The list of all chunks in the cache
|
||||
*/
|
||||
public Collection<ChunkData> getAllChunks(){
|
||||
return this.cacheMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the world position of a chunk
|
||||
* @param chunk The chunk
|
||||
* @return The world position of the chunk
|
||||
*/
|
||||
public Vector3i getChunkPosition(ChunkData chunk){
|
||||
return chunkPositionMap.get(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
package electrosphere.client.terrain.cache;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.game.terrain.processing.TerrainInterpolator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class LoadingChunk {
|
||||
|
||||
int worldX, worldY;
|
||||
int totalNumMessages;
|
||||
int numReceivedMessages = 0;
|
||||
|
||||
ClientWorldData clientWorldData;
|
||||
|
||||
float[][] macroValues;
|
||||
long[][] randomizer;
|
||||
List<ChunkModification> modification = new ArrayList<ChunkModification>();
|
||||
|
||||
public LoadingChunk(int worldX, int worldY, int numMessages, ClientWorldData clientWorldData){
|
||||
this.worldX = worldX;
|
||||
this.worldY = worldY;
|
||||
this.totalNumMessages = numMessages;
|
||||
this.clientWorldData = clientWorldData;
|
||||
}
|
||||
|
||||
public void addMacroValues(float[][] macroValues){
|
||||
this.macroValues = macroValues;
|
||||
}
|
||||
|
||||
public void addRandomizer(long[][] randomizer){
|
||||
this.randomizer = randomizer;
|
||||
}
|
||||
|
||||
public void addModification(int worldX, int worldY, int locationX, int locationY, float value){
|
||||
// System.out.println("Client add modification");
|
||||
ChunkModification newModification = new ChunkModification(worldX, worldY, locationX, locationY, value);
|
||||
modification.add(newModification);
|
||||
}
|
||||
|
||||
public void incrementMessageCount(){
|
||||
numReceivedMessages++;
|
||||
}
|
||||
|
||||
public boolean isComplete(){
|
||||
return numReceivedMessages >= totalNumMessages && macroValues != null && randomizer != null;
|
||||
}
|
||||
|
||||
public float[][] exportFloats(){
|
||||
float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk(
|
||||
macroValues,
|
||||
randomizer,
|
||||
clientWorldData.getDynamicInterpolationRatio(),
|
||||
clientWorldData.getRandomDampener()
|
||||
);
|
||||
if(modification != null){
|
||||
for(ChunkModification modification : modification){
|
||||
// System.out.println("Apply modification");
|
||||
heightmap = modification.applyModification(heightmap);
|
||||
}
|
||||
}
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
public int getWorldX() {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
public int getWorldY() {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
public ClientWorldData getClientWorldData(){
|
||||
return this.clientWorldData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
package electrosphere.client.terrain.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class LoadingChunkCache {
|
||||
Map<String,LoadingChunk> cacheMap = new ConcurrentHashMap<String,LoadingChunk>();
|
||||
List<String> cacheList = new CopyOnWriteArrayList<String>();
|
||||
|
||||
|
||||
public LoadingChunkCache(){
|
||||
}
|
||||
|
||||
|
||||
//USED TO ADD macro values
|
||||
public void addLoadingChunkToCache(LoadingChunk chunk){
|
||||
cacheMap.put(getKey(chunk.worldX, chunk.worldY),chunk);
|
||||
}
|
||||
|
||||
public LoadingChunk fetch(String key){
|
||||
return cacheMap.get(key);
|
||||
}
|
||||
|
||||
|
||||
public String getKey(int worldX, int worldY){
|
||||
return worldX + "-" + worldY;
|
||||
}
|
||||
|
||||
public boolean containsKey(String key){
|
||||
return cacheMap.containsKey(key);
|
||||
}
|
||||
|
||||
public Collection<LoadingChunk> getChunks(){
|
||||
return cacheMap.values();
|
||||
}
|
||||
|
||||
public void remove(LoadingChunk chunk){
|
||||
cacheMap.remove(getKey(chunk.worldX,chunk.worldY));
|
||||
cacheList.remove(getKey(chunk.worldX,chunk.worldY));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
package electrosphere.client.terrain.cells;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class ClientDataCell {
|
||||
List<Entity> entities = new LinkedList<Entity>();
|
||||
|
||||
|
||||
public ClientDataCell(){
|
||||
|
||||
}
|
||||
|
||||
public void addEntity(Entity e){
|
||||
entities.add(e);
|
||||
}
|
||||
|
||||
public void removeEntity(Entity e){
|
||||
entities.remove(e);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,49 +1,35 @@
|
||||
package electrosphere.client.terrain.cells;
|
||||
|
||||
import electrosphere.collision.dispatch.CollisionObject;
|
||||
import electrosphere.dynamics.RigidBody;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.game.collision.PhysicsUtils;
|
||||
import electrosphere.game.collision.collidable.Collidable;
|
||||
import electrosphere.game.terrain.processing.TerrainInterpolator;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.Mesh;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.ModelUtils;
|
||||
import electrosphere.renderer.RenderUtils;
|
||||
import electrosphere.entity.types.terrain.TerrainChunk;
|
||||
import electrosphere.renderer.ShaderProgram;
|
||||
import electrosphere.renderer.actor.ActorTextureMask;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
import electrosphere.util.Utilities;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author satellite
|
||||
*/
|
||||
public class DrawCell {
|
||||
int cellX;
|
||||
int cellY;
|
||||
|
||||
float[][] heightmap;
|
||||
float[][] texturemap;
|
||||
|
||||
int dynamicInterpolationRatio;
|
||||
//the position of the draw cell in world coordinates
|
||||
Vector3i worldPos;
|
||||
|
||||
Entity modelEntity;
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
CollisionObject physicsObject;
|
||||
DBody physicsObject;
|
||||
|
||||
//Allocated once instead of continuously, used to generate the visual/physics models
|
||||
float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE];
|
||||
int[][][] types = new int[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE];
|
||||
|
||||
static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png");
|
||||
static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png");
|
||||
@ -64,84 +50,177 @@ public class DrawCell {
|
||||
|
||||
|
||||
/**
|
||||
* Catches for this function: drawArray's dimensions need to be equal to drawWidth and 1 greater than cellWidth
|
||||
* because the model creation code for terrain heightmaps sucks :D
|
||||
* 06/03/2021
|
||||
*
|
||||
*
|
||||
* ITS NOT EVEN UP TO DATE!
|
||||
* 06/13/2021
|
||||
*
|
||||
* //@param drawArray
|
||||
* Constructs a drawcell object
|
||||
*/
|
||||
public static DrawCell generateTerrainCell(
|
||||
int cellX,
|
||||
int cellY,
|
||||
float[][] heightmap,
|
||||
float[][] texturemap,
|
||||
int dynamicInterpolationRatio,
|
||||
ShaderProgram program
|
||||
Vector3i worldPos
|
||||
){
|
||||
DrawCell rVal = new DrawCell();
|
||||
rVal.cellX = cellX;
|
||||
rVal.cellY = cellY;
|
||||
rVal.program = program;
|
||||
rVal.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
rVal.heightmap = heightmap;
|
||||
rVal.texturemap = texturemap;
|
||||
rVal.worldPos = worldPos;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a drawable entity based on this chunk
|
||||
* @param stride The stride between indices used to generate "sparse" meshes
|
||||
*/
|
||||
public void generateDrawableEntity(int stride){
|
||||
public void generateDrawableEntity(){
|
||||
if(modelEntity != null){
|
||||
Globals.clientScene.deregisterEntity(modelEntity);
|
||||
}
|
||||
Model terrainModel = RenderUtils.createMinimizedTerrainModelPrecomputedShader(heightmap, texturemap, program, stride);
|
||||
Mesh terrainMesh = terrainModel.meshes.get(0);
|
||||
List<Texture> textureList = new LinkedList<Texture>();
|
||||
textureList.add(groundTextureOne);
|
||||
textureList.add(groundTextureTwo);
|
||||
textureList.add(groundTextureThree);
|
||||
textureList.add(groundTextureFour);
|
||||
List<String> uniformList = new LinkedList<String>();
|
||||
uniformList.add("groundTextures[0]");
|
||||
uniformList.add("groundTextures[1]");
|
||||
uniformList.add("groundTextures[2]");
|
||||
uniformList.add("groundTextures[3]");
|
||||
String terrainModelPath = Globals.assetManager.registerModel(terrainModel);
|
||||
modelEntity = EntityCreationUtils.createClientSpatialEntity();
|
||||
EntityCreationUtils.makeEntityDrawable(modelEntity, terrainModelPath);
|
||||
EntityUtils.getActor(modelEntity).addTextureMask(new ActorTextureMask("terrain",textureList,uniformList));
|
||||
modelEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
modelEntity.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
|
||||
LoggerInterface.loggerRenderer.INFO("New cell @ " + cellX * dynamicInterpolationRatio + "," + cellY * dynamicInterpolationRatio);
|
||||
EntityUtils.getPosition(modelEntity).set(new Vector3f(cellX * dynamicInterpolationRatio, 0.0f, cellY * dynamicInterpolationRatio));
|
||||
|
||||
fillInData();
|
||||
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(weights, types, 0);
|
||||
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
|
||||
}
|
||||
|
||||
protected Vector3d getRealPos(){
|
||||
return new Vector3d(
|
||||
worldPos.x * ChunkData.CHUNK_SIZE,
|
||||
worldPos.y * ChunkData.CHUNK_SIZE,
|
||||
worldPos.z * ChunkData.CHUNK_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
public void retireCell(){
|
||||
/**
|
||||
* Destroys a drawcell including its physics
|
||||
*/
|
||||
public void destroy(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
collisionEngine.destroyEntityThatHasPhysics(modelEntity);
|
||||
EntityUtils.cleanUpEntity(modelEntity);
|
||||
}
|
||||
|
||||
public void generatePhysics(){
|
||||
//if we're in no-graphics mode, need to generate the entity
|
||||
if(modelEntity == null){
|
||||
modelEntity = EntityCreationUtils.createClientSpatialEntity();
|
||||
modelEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
EntityUtils.getPosition(modelEntity).set(new Vector3f(cellX * dynamicInterpolationRatio, 0.0f, cellY * dynamicInterpolationRatio));
|
||||
|
||||
|
||||
/**
|
||||
* Fills in the internal arrays of data for generate terrain models
|
||||
*/
|
||||
private void fillInData(){
|
||||
//
|
||||
//fill in data
|
||||
//
|
||||
//main chunk
|
||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
|
||||
weights[x][y][z] = currentChunk.getWeight(x,y,z);
|
||||
types[x][y][z] = currentChunk.getType(x,y,z);
|
||||
}
|
||||
}
|
||||
}
|
||||
//face X
|
||||
if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getWeight(0, i, j);
|
||||
types[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getType(0, i, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[ChunkData.CHUNK_SIZE][i][j] = 0;
|
||||
types[ChunkData.CHUNK_SIZE][i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//face Y
|
||||
if(worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getWeight(i, 0, j);
|
||||
types[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getType(i, 0, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[i][ChunkData.CHUNK_SIZE][j] = 0;
|
||||
types[i][ChunkData.CHUNK_SIZE][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//face Z
|
||||
if(worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z + 1);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, j, 0);
|
||||
types[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, j, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
|
||||
weights[i][j][ChunkData.CHUNK_SIZE] = 0;
|
||||
types[i][j][ChunkData.CHUNK_SIZE] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//edge X-Y
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getWeight(0, 0, i);
|
||||
types [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getType(0, 0, i);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = 0;
|
||||
types [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = 0;
|
||||
}
|
||||
}
|
||||
//edge X-Z
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z + 1);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, i, 0);
|
||||
types [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, i, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = 0;
|
||||
types [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = 0;
|
||||
}
|
||||
}
|
||||
//edge Y-Z
|
||||
if(
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z + 1);
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, 0, 0);
|
||||
types [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, 0, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
|
||||
weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
|
||||
types [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
|
||||
}
|
||||
}
|
||||
if(
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
|
||||
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, 0, 0);
|
||||
types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, 0, 0);
|
||||
} else {
|
||||
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
|
||||
types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
|
||||
}
|
||||
//then actually perform the attach
|
||||
physicsObject = PhysicsUtils.attachTerrainRigidBody(modelEntity,heightmap,false);
|
||||
Globals.clientSceneWrapper.getCollisionEngine().registerPhysicsEntity(modelEntity);
|
||||
// System.out.println("generate physics");
|
||||
}
|
||||
|
||||
public void destroyPhysics(){
|
||||
Globals.clientSceneWrapper.getCollisionEngine().deregisterCollidableEntity(modelEntity);
|
||||
Globals.clientSceneWrapper.getCollisionEngine().deregisterRigidBody((RigidBody)physicsObject);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
package electrosphere.client.terrain.cells;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.terrain.processing.TerrainInterpolator;
|
||||
import electrosphere.game.collision.CommonWorldData;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.ShaderProgram;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -26,41 +25,40 @@ public class DrawCellManager {
|
||||
//the center of this cell manager's array in cell space
|
||||
int cellX;
|
||||
int cellY;
|
||||
int cellZ;
|
||||
|
||||
|
||||
//the dimensions of the world that this cell manager can handles
|
||||
int cellWidth;
|
||||
int cellHeight;
|
||||
|
||||
//the width of a minicell in this manager
|
||||
int miniCellWidth;
|
||||
|
||||
//all currently displaying mini cells
|
||||
DrawCell[][] cells;
|
||||
boolean[][] valid;
|
||||
boolean[][] drawable;
|
||||
boolean[][] updateable;
|
||||
boolean[][] hasRequested;
|
||||
boolean[][] needsPhysics;
|
||||
boolean[][] hasPhysics;
|
||||
Set<DrawCell> cells;
|
||||
Map<String,DrawCell> keyCellMap = new HashMap<String,DrawCell>();
|
||||
Set<String> hasNotRequested;
|
||||
Set<String> hasRequested;
|
||||
Set<String> drawable;
|
||||
Set<String> undrawable;
|
||||
Set<String> updateable;
|
||||
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
|
||||
|
||||
int drawRadius = 5;
|
||||
// int drawRadius = 5;
|
||||
int drawStepdownInterval = 3;
|
||||
int drawStepdownValue = 25;
|
||||
|
||||
double drawRadius = 50;
|
||||
|
||||
int physicsRadius = 3;
|
||||
|
||||
int worldBoundDiscreteMin = 0;
|
||||
int worldBoundDiscreteMax = 0;
|
||||
|
||||
//metadata about the game world
|
||||
CommonWorldData commonWorldData;
|
||||
|
||||
|
||||
//client terrain manager
|
||||
// ClientTerrainManager clientTerrainManager;
|
||||
|
||||
@ -80,600 +78,291 @@ public class DrawCellManager {
|
||||
* @param discreteX The initial discrete position X coordinate
|
||||
* @param discreteY The initial discrete position Y coordinate
|
||||
*/
|
||||
public DrawCellManager(CommonWorldData commonWorldData, ClientTerrainManager clientTerrainManager, int discreteX, int discreteY){
|
||||
this.commonWorldData = commonWorldData;
|
||||
worldBoundDiscreteMax = (int)(commonWorldData.getWorldBoundMin().x / commonWorldData.getDynamicInterpolationRatio() * 1.0f);
|
||||
// this.clientTerrainManager = clientTerrainManager;
|
||||
cells = new DrawCell[drawRadius * 2 + 1][drawRadius * 2 + 1];
|
||||
valid = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1];
|
||||
drawable = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1];
|
||||
updateable = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1];
|
||||
hasRequested = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1];
|
||||
needsPhysics = new boolean[physicsRadius * 2 + 1][physicsRadius * 2 + 1];
|
||||
hasPhysics = new boolean[physicsRadius * 2 + 1][physicsRadius * 2 + 1];
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
valid[x][y] = false;
|
||||
drawable[x][y] = false;
|
||||
updateable[x][y] = false;
|
||||
hasRequested[x][y] = false;
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
needsPhysics[x][y] = true;
|
||||
hasPhysics[x][y] = false;
|
||||
}
|
||||
}
|
||||
public DrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
|
||||
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f);
|
||||
cells = new HashSet<DrawCell>();
|
||||
hasNotRequested = new HashSet<String>();
|
||||
drawable = new HashSet<String>();
|
||||
undrawable = new HashSet<String>();
|
||||
updateable = new HashSet<String>();
|
||||
hasRequested = new HashSet<String>();
|
||||
|
||||
cellX = discreteX;
|
||||
cellY = discreteY;
|
||||
cellZ = discreteZ;
|
||||
|
||||
program = Globals.terrainShaderProgram;
|
||||
|
||||
drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius();
|
||||
// drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius();
|
||||
drawStepdownInterval = Globals.userSettings.getGameplayPhysicsCellRadius();
|
||||
physicsRadius = Globals.userSettings.getGameplayPhysicsCellRadius();
|
||||
|
||||
invalidateAllCells();
|
||||
|
||||
update = true;
|
||||
}
|
||||
|
||||
DrawCellManager(){
|
||||
|
||||
}
|
||||
|
||||
public int getCellX(){
|
||||
return cellX;
|
||||
}
|
||||
|
||||
public int getCellY(){
|
||||
return cellY;
|
||||
}
|
||||
|
||||
public void setCellX(int x){
|
||||
cellX = x;
|
||||
}
|
||||
|
||||
public void setCellY(int y){
|
||||
cellY = y;
|
||||
}
|
||||
|
||||
public void setCell(Vector2i cellPos){
|
||||
public void setCell(Vector3i cellPos){
|
||||
cellX = cellPos.x;
|
||||
cellY = cellPos.y;
|
||||
cellZ = cellPos.z;
|
||||
}
|
||||
|
||||
void updateInvalidCell(){
|
||||
int targetX = 0;
|
||||
int targetY = 0;
|
||||
boolean found = false;
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
targetX = x;
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
targetY = y;
|
||||
if(!valid[x][y]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found){
|
||||
int currentCellX = cellX - drawRadius + targetX;
|
||||
int currentCellY = cellY - drawRadius + targetY;
|
||||
|
||||
|
||||
if(
|
||||
currentCellX >= 0 &&
|
||||
currentCellX < commonWorldData.getWorldDiscreteSize() &&
|
||||
currentCellY >= 0 &&
|
||||
currentCellY < commonWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
if(containsHeightmapAtDiscretePoint(currentCellX, currentCellY)){
|
||||
cells[targetX][targetY] = DrawCell.generateTerrainCell(
|
||||
currentCellX,
|
||||
currentCellY,
|
||||
getHeightmapAtPoint(currentCellX, currentCellY),
|
||||
getTextureMapAtPoint(currentCellX, currentCellY),
|
||||
commonWorldData.getDynamicInterpolationRatio(),
|
||||
program
|
||||
);
|
||||
valid[targetX][targetY] = true;
|
||||
drawable[targetX][targetY] = false;
|
||||
updateable[targetX][targetY] = false;
|
||||
hasRequested[targetX][targetY] = false;
|
||||
hasPhysics[targetX][targetY] = false;
|
||||
needsPhysics[targetX][targetY] = true;
|
||||
// if(Math.abs(physicsRadius + 1 - targetX) < physicsRadius && Math.abs(physicsRadius + 1 - targetY) < physicsRadius){
|
||||
// needsPhysics[targetX][targetY] = true;
|
||||
// }
|
||||
} else {
|
||||
if(hasRequested[targetX][targetY] == false){
|
||||
//client should request macro values from server
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkMessage(currentCellX, currentCellY));
|
||||
hasRequested[targetX][targetY] = true;
|
||||
void updateUnrequestedCell(){
|
||||
if(hasNotRequested.size() > 0){
|
||||
String targetKey = hasNotRequested.iterator().next();
|
||||
hasNotRequested.remove(targetKey);
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
// Vector3i vector = getVectorFromKey(targetKey);
|
||||
// int currentCellX = cellX - drawRadius + vector.x;
|
||||
// int currentCellY = cellY - drawRadius + vector.y;
|
||||
// int currentCellZ = cellZ - drawRadius + vector.z;
|
||||
for(int i = 0; i < 2; i++){
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int k = 0; k < 2; k++){
|
||||
Vector3i posToCheck = new Vector3i(worldPos).add(i,j,k);
|
||||
if(
|
||||
posToCheck.x >= 0 &&
|
||||
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
posToCheck.y >= 0 &&
|
||||
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
posToCheck.z >= 0 &&
|
||||
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
|
||||
){
|
||||
// if(!hasRequested.contains(targetKey)){
|
||||
//client should request chunk data from server for each chunk necessary to create the model
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
|
||||
posToCheck.x,
|
||||
posToCheck.y,
|
||||
posToCheck.z
|
||||
));
|
||||
// }
|
||||
}
|
||||
undrawable.add(targetKey);
|
||||
hasRequested.add(targetKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid[targetX][targetY] = true;
|
||||
drawable[targetX][targetY] = false;
|
||||
updateable[targetX][targetY] = false;
|
||||
hasRequested[targetX][targetY] = false;
|
||||
hasPhysics[targetX][targetY] = false;
|
||||
needsPhysics[targetX][targetY] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes one of the undrawable cells drawable
|
||||
*/
|
||||
void makeCellDrawable(){
|
||||
int targetX = 0;
|
||||
int targetY = 0;
|
||||
boolean found = false;
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
targetX = x;
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
targetY = y;
|
||||
if(valid[x][y] && !drawable[x][y]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found){
|
||||
int currentCellX = cellX - drawRadius + targetX;
|
||||
int currentCellY = cellY - drawRadius + targetY;
|
||||
if(
|
||||
currentCellX >= 0 &&
|
||||
currentCellX < commonWorldData.getWorldDiscreteSize() &&
|
||||
currentCellY >= 0 &&
|
||||
currentCellY < commonWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
//physics radius calculation
|
||||
// if(Math.abs(physicsRadius + 1 - targetX) < physicsRadius && Math.abs(physicsRadius + 1 - targetY) < physicsRadius){
|
||||
// needsPhysics[targetX][targetY] = true;
|
||||
// }
|
||||
//calculation for stride
|
||||
int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius));//Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius);
|
||||
int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue));
|
||||
while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){
|
||||
stride = stride + 1;
|
||||
if(undrawable.size() > 0){
|
||||
String targetKey = undrawable.iterator().next();
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
boolean containsNecessaryChunks = true;
|
||||
for(int i = 0; i < 2; i++){
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int k = 0; k < 2; k++){
|
||||
Vector3i posToCheck = new Vector3i(worldPos).add(i,j,k);
|
||||
if(worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
!containsChunkDataAtWorldPoint(posToCheck.x,posToCheck.y,posToCheck.z)
|
||||
){
|
||||
containsNecessaryChunks = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//make drawable entity
|
||||
cells[targetX][targetY].generateDrawableEntity(stride);
|
||||
}
|
||||
drawable[targetX][targetY] = true;
|
||||
//if contains all chunks necessary to generate visuals
|
||||
if(containsNecessaryChunks){
|
||||
//build float array
|
||||
DrawCell cell = DrawCell.generateTerrainCell(
|
||||
worldPos
|
||||
);
|
||||
cells.add(cell);
|
||||
keyCellMap.put(targetKey,cell);
|
||||
// undrawable.add(targetKey);
|
||||
undrawable.remove(targetKey);
|
||||
drawable.add(targetKey);
|
||||
//make drawable entity
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
//evaluate for foliage
|
||||
Globals.clientFoliageManager.evaluateChunk(worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a cell that can be updated
|
||||
*/
|
||||
void updateCellModel(){
|
||||
int targetX = 0;
|
||||
int targetY = 0;
|
||||
boolean found = false;
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
targetX = x;
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
targetY = y;
|
||||
if(updateable[x][y]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found){
|
||||
int currentCellX = cellX - drawRadius + targetX;
|
||||
int currentCellY = cellY - drawRadius + targetY;
|
||||
if(updateable.size() > 0){
|
||||
String targetKey = updateable.iterator().next();
|
||||
updateable.remove(targetKey);
|
||||
Vector3i worldPos = getVectorFromKey(targetKey);
|
||||
if(
|
||||
currentCellX >= 0 &&
|
||||
currentCellX < commonWorldData.getWorldDiscreteSize() &&
|
||||
currentCellY >= 0 &&
|
||||
currentCellY < commonWorldData.getWorldDiscreteSize()
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
// if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){
|
||||
// needsPhysics[targetX][targetY] = true;
|
||||
// }
|
||||
int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius);
|
||||
int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue));
|
||||
while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){
|
||||
stride = stride + 1;
|
||||
}
|
||||
cells[targetX][targetY].generateDrawableEntity(stride);
|
||||
// int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius);
|
||||
// int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue));
|
||||
// while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){
|
||||
// stride = stride + 1;
|
||||
// }
|
||||
keyCellMap.get(targetKey).destroy();
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
}
|
||||
updateable[targetX][targetY] = false;
|
||||
drawable[targetX][targetY] = true;
|
||||
drawable.add(targetKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean containsInvalidCell(){
|
||||
// if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){
|
||||
for(int x = 0;x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
if(!valid[x][y]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// } else if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){
|
||||
// return cells[0][0] == null || cells[1][0] == null || cells[0][1] == null | cells[1][1] == null;
|
||||
// }
|
||||
return false;
|
||||
public boolean containsUnrequestedCell(){
|
||||
return hasNotRequested.size() > 0;
|
||||
}
|
||||
|
||||
public boolean containsUndrawableCell(){
|
||||
for(int x = 0;x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
if(!drawable[x][y] && this.generateDrawables){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return undrawable.size() > 0;
|
||||
}
|
||||
|
||||
public boolean containsUpdateableCell(){
|
||||
for(int x = 0;x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
if(updateable[x][y] && this.generateDrawables){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsPhysicsNeedingCell(){
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
if(needsPhysics[x][y]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPhysicsToCell(){
|
||||
int targetX = 0;
|
||||
int targetY = 0;
|
||||
boolean found = false;
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
targetX = x;
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
targetY = y;
|
||||
// System.out.println(x + " <=>w " + y);
|
||||
if(needsPhysics[x][y]){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found){
|
||||
int currentCellX = cellX - physicsRadius + targetX;
|
||||
int currentCellY = cellY - physicsRadius + targetY;
|
||||
if(
|
||||
currentCellX >= 0 &&
|
||||
currentCellX < commonWorldData.getWorldDiscreteSize() &&
|
||||
currentCellY >= 0 &&
|
||||
currentCellY < commonWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
// if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){
|
||||
// needsPhysics[targetX][targetY] = true;
|
||||
// }
|
||||
// int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius);
|
||||
// int stride = Math.min(clientWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue));
|
||||
// while(clientWorldData.getDynamicInterpolationRatio() % stride != 0){
|
||||
// stride = stride + 1;
|
||||
// }
|
||||
// if(cells[targetX][targetY + drawRadius] != null){
|
||||
// System.out.println(targetX + " - " + targetY);
|
||||
cells[targetX + drawRadius - physicsRadius][targetY + drawRadius - physicsRadius].generatePhysics();
|
||||
// } else {
|
||||
// System.out.println("Current cell is null: " + currentCellX + " - " + currentCellY);
|
||||
// }
|
||||
}
|
||||
needsPhysics[targetX][targetY] = false;
|
||||
hasPhysics[targetX][targetY] = true;
|
||||
}
|
||||
return updateable.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
public void shiftChunksNegX(){
|
||||
//retire old graphics
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
if(cells[drawRadius * 2][y] != null){
|
||||
cells[drawRadius * 2][y].retireCell();
|
||||
}
|
||||
}
|
||||
//shift draw array
|
||||
for(int x = drawRadius * 2; x > 0; x--){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
cells[x][y] = cells[x-1][y];
|
||||
updateable[x][y] = true;
|
||||
hasRequested[x][y] = hasRequested[x-1][y];
|
||||
}
|
||||
}
|
||||
//invalidate edge of draw array
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
valid[0][y] = false;
|
||||
hasRequested[0][y] = false;
|
||||
}
|
||||
//retire physics of cells
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
if(hasPhysics[x][physicsRadius * 2]){
|
||||
if(cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius] != null){
|
||||
cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics();
|
||||
}
|
||||
}
|
||||
}
|
||||
//shift physics array
|
||||
for(int x = physicsRadius * 2; x > 0; x--){
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
needsPhysics[x][y] = needsPhysics[x-1][y];
|
||||
hasPhysics[x][y] = hasPhysics[x-1][y];
|
||||
}
|
||||
}
|
||||
//invalidate edge of physics array
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
needsPhysics[0][y] = true;
|
||||
hasPhysics[0][y] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void shiftChunksPosX(){
|
||||
//retire old graphics
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
if(cells[0][y] != null){
|
||||
cells[0][y].retireCell();
|
||||
}
|
||||
}
|
||||
//shift draw array
|
||||
for(int x = 0; x < drawRadius * 2; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
cells[x][y] = cells[x+1][y];
|
||||
updateable[x][y] = true;
|
||||
hasRequested[x][y] = hasRequested[x+1][y];
|
||||
}
|
||||
}
|
||||
//invalidate edge of draw array
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
valid[drawRadius * 2][y] = false;
|
||||
hasRequested[drawRadius * 2][y] = false;
|
||||
}
|
||||
//retire physics of cells
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
if(hasPhysics[x][physicsRadius * 2]){
|
||||
if(cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius] != null){
|
||||
cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics();
|
||||
}
|
||||
}
|
||||
}
|
||||
//shift physics array
|
||||
for(int x = 0; x < physicsRadius * 2; x++){
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
needsPhysics[x][y] = needsPhysics[x+1][y];
|
||||
hasPhysics[x][y] = hasPhysics[x+1][y];
|
||||
}
|
||||
}
|
||||
//invalidate edge of physics array
|
||||
for(int y = 0; y < physicsRadius * 2 + 1; y++){
|
||||
needsPhysics[physicsRadius * 2][y] = true;
|
||||
hasPhysics[physicsRadius * 2][y] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void shiftChunksNegY(){
|
||||
//retire cells
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
if(cells[x][drawRadius * 2] != null){
|
||||
cells[x][drawRadius * 2].retireCell();
|
||||
}
|
||||
}
|
||||
//shift draw array
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
for(int y = drawRadius * 2; y > 0; y--){
|
||||
cells[x][y] = cells[x][y-1];
|
||||
updateable[x][y] = true;
|
||||
hasRequested[x][y] = hasRequested[x][y-1];
|
||||
}
|
||||
}
|
||||
//invalidate edge of draw array
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
valid[x][0] = false;
|
||||
hasRequested[x][0] = false;
|
||||
}
|
||||
//retire physics of cells
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
if(hasPhysics[x][physicsRadius * 2]){
|
||||
if(cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius] != null){
|
||||
cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics();
|
||||
}
|
||||
}
|
||||
}
|
||||
//shift physics array
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
for(int y = physicsRadius * 2; y > 0; y--){
|
||||
needsPhysics[x][y] = needsPhysics[x][y-1];
|
||||
hasPhysics[x][y] = hasPhysics[x][y-1];
|
||||
}
|
||||
}
|
||||
//invalidate edge of physics array
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
needsPhysics[x][0] = true;
|
||||
hasPhysics[x][0] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void shiftChunksPosY(){
|
||||
//retire old graphics
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
if(cells[x][0] != null){
|
||||
cells[x][0].retireCell();
|
||||
}
|
||||
}
|
||||
//shift draw array
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2; y++){
|
||||
cells[x][y] = cells[x][y+1];
|
||||
updateable[x][y] = true;
|
||||
hasRequested[x][y] = hasRequested[x][y+1];
|
||||
}
|
||||
}
|
||||
//invalidate edge of draw array
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
valid[x][drawRadius * 2] = false;
|
||||
hasRequested[x][drawRadius * 2] = false;
|
||||
}
|
||||
//retire physics of cells
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
if(hasPhysics[x][0]){
|
||||
if(cells[x + drawRadius - physicsRadius][0 + drawRadius - physicsRadius] != null){
|
||||
cells[x + drawRadius - physicsRadius][0 + drawRadius - physicsRadius].destroyPhysics();
|
||||
}
|
||||
}
|
||||
}
|
||||
//shift physics array
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < physicsRadius * 2; y++){
|
||||
needsPhysics[x][y] = needsPhysics[x][y+1];
|
||||
hasPhysics[x][y] = hasPhysics[x][y+1];
|
||||
}
|
||||
}
|
||||
//invalidate edge of physics array
|
||||
for(int x = 0; x < physicsRadius * 2 + 1; x++){
|
||||
needsPhysics[x][physicsRadius * 2] = true;
|
||||
hasPhysics[x][physicsRadius * 2] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int transformRealSpaceToCellSpace(double input){
|
||||
return (int)(input / commonWorldData.getDynamicInterpolationRatio());
|
||||
return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the valid set and adds all keys to invalid set
|
||||
*/
|
||||
public void invalidateAllCells(){
|
||||
for(int x = 0; x < drawRadius * 2 + 1; x++){
|
||||
for(int y = 0; y < drawRadius * 2 + 1; y++){
|
||||
valid[x][y] = false;
|
||||
drawable.clear();
|
||||
hasNotRequested.clear();
|
||||
clearOutOfBoundsCells();
|
||||
queueNewCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates whether the position of the player has changed and if so, invalidates and cleans up cells accordingly
|
||||
* @param position The position of the player entity on current frame
|
||||
*/
|
||||
public void calculateDeltas(Vector3d position){
|
||||
//check if any not requested cells no longer need to be requested
|
||||
clearOutOfBoundsCells();
|
||||
//check if any cells should be added
|
||||
queueNewCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cells outside of draw radius
|
||||
*/
|
||||
private void clearOutOfBoundsCells(){
|
||||
Set<DrawCell> cellsToRemove = new HashSet<DrawCell>();
|
||||
for(DrawCell cell : cells){
|
||||
Vector3d realPos = cell.getRealPos();
|
||||
if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){
|
||||
cellsToRemove.add(cell);
|
||||
}
|
||||
}
|
||||
for(DrawCell cell : cellsToRemove){
|
||||
cells.remove(cell);
|
||||
String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z);
|
||||
hasNotRequested.remove(key);
|
||||
drawable.remove(key);
|
||||
undrawable.remove(key);
|
||||
updateable.remove(key);
|
||||
keyCellMap.remove(key);
|
||||
hasRequested.remove(key);
|
||||
cell.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues new cells that are in bounds but not currently accounted for
|
||||
*/
|
||||
private void queueNewCells(){
|
||||
if(Globals.playerEntity != null && Globals.clientWorldData != null){
|
||||
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
for(int x = -(int)drawRadius; x < drawRadius; x = x + ChunkData.CHUNK_SIZE){
|
||||
for(int y = -(int)drawRadius; y < drawRadius; y = y + ChunkData.CHUNK_SIZE){
|
||||
for(int z = -(int)drawRadius; z < drawRadius; z = z + ChunkData.CHUNK_SIZE){
|
||||
Vector3d newPos = new Vector3d(playerPos.x + x, playerPos.y + y, playerPos.z + z);
|
||||
Vector3i worldPos = new Vector3i(
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.x),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.y),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(newPos.z)
|
||||
);
|
||||
Vector3d chunkRealSpace = new Vector3d(
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.x),
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.y),
|
||||
Globals.clientWorldData.convertChunkToRealSpace(worldPos.z)
|
||||
);
|
||||
if(
|
||||
playerPos.distance(chunkRealSpace) < drawRadius &&
|
||||
worldPos.x >= 0 &&
|
||||
worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y >= 0 &&
|
||||
worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z >= 0 &&
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
String key = getCellKey(
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.x),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.y),
|
||||
Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.z)
|
||||
);
|
||||
if(!keyCellMap.containsKey(key) && !hasNotRequested.contains(key) && !undrawable.contains(key) && !drawable.contains(key) &&
|
||||
!hasRequested.contains(key)){
|
||||
hasNotRequested.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void calculateDeltas(Vector3d oldPosition, Vector3d newPosition){
|
||||
// if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){
|
||||
if(transformRealSpaceToCellSpace(newPosition.x()) < transformRealSpaceToCellSpace(oldPosition.x())){
|
||||
shiftChunksNegX();
|
||||
setCellX(transformRealSpaceToCellSpace(newPosition.x()));
|
||||
setCellY(transformRealSpaceToCellSpace(newPosition.z()));
|
||||
} else if(transformRealSpaceToCellSpace(newPosition.x()) > transformRealSpaceToCellSpace(oldPosition.x())){
|
||||
shiftChunksPosX();
|
||||
setCellX(transformRealSpaceToCellSpace(newPosition.x()));
|
||||
setCellY(transformRealSpaceToCellSpace(newPosition.z()));
|
||||
}
|
||||
|
||||
if(transformRealSpaceToCellSpace(newPosition.z()) < transformRealSpaceToCellSpace(oldPosition.z())){
|
||||
shiftChunksNegY();
|
||||
setCellX(transformRealSpaceToCellSpace(newPosition.x()));
|
||||
setCellY(transformRealSpaceToCellSpace(newPosition.z()));
|
||||
} else if(transformRealSpaceToCellSpace(newPosition.z()) > transformRealSpaceToCellSpace(oldPosition.z())){
|
||||
shiftChunksPosY();
|
||||
setCellX(transformRealSpaceToCellSpace(newPosition.x()));
|
||||
setCellY(transformRealSpaceToCellSpace(newPosition.z()));
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates cells that need updating in this manager
|
||||
*/
|
||||
public void update(){
|
||||
if(update){
|
||||
// if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){
|
||||
if(containsInvalidCell()){
|
||||
updateInvalidCell();
|
||||
} else if(containsUndrawableCell()){
|
||||
makeCellDrawable();
|
||||
} else if(containsUpdateableCell()){
|
||||
updateCellModel();
|
||||
} else if(containsPhysicsNeedingCell()){
|
||||
addPhysicsToCell();
|
||||
}
|
||||
if(containsUnrequestedCell() && !containsUndrawableCell()){
|
||||
updateUnrequestedCell();
|
||||
} else if(containsUndrawableCell()){
|
||||
makeCellDrawable();
|
||||
} else if(containsUpdateableCell()){
|
||||
updateCellModel();
|
||||
}
|
||||
// } else if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){
|
||||
// if(cells[0][0] == null){
|
||||
// int arenaChunkWidth = 100;
|
||||
// cells[0][0] = new DrawCell(
|
||||
// new float[arenaChunkWidth + 1][arenaChunkWidth + 1],
|
||||
// arenaChunkWidth + 1,
|
||||
// 0,
|
||||
// 0,
|
||||
// arenaChunkWidth,
|
||||
// program
|
||||
// );
|
||||
// cells[0][0].generateDrawableEntity(1);
|
||||
// } else if(cells[0][1] == null){
|
||||
// int arenaChunkWidth = 100;
|
||||
// cells[0][1] = new DrawCell(
|
||||
// new float[arenaChunkWidth + 1][arenaChunkWidth + 1],
|
||||
// arenaChunkWidth + 1,
|
||||
// 0,
|
||||
// 1,
|
||||
// arenaChunkWidth,
|
||||
// program
|
||||
// );
|
||||
// cells[0][1].generateDrawableEntity(1);
|
||||
// } else if(cells[1][0] == null){
|
||||
// int arenaChunkWidth = 100;
|
||||
// cells[1][0] = new DrawCell(
|
||||
// new float[arenaChunkWidth + 1][arenaChunkWidth + 1],
|
||||
// arenaChunkWidth + 1,
|
||||
// 1,
|
||||
// 0,
|
||||
// arenaChunkWidth,
|
||||
// program
|
||||
// );
|
||||
// cells[1][0].generateDrawableEntity(1);
|
||||
// } else if(cells[1][1] == null){
|
||||
// int arenaChunkWidth = 100;
|
||||
// cells[1][1] = new DrawCell(
|
||||
// new float[arenaChunkWidth + 1][arenaChunkWidth + 1],
|
||||
// arenaChunkWidth + 1,
|
||||
// 1,
|
||||
// 1,
|
||||
// arenaChunkWidth,
|
||||
// program
|
||||
// );
|
||||
// cells[1][1].generateDrawableEntity(1);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// public float getElevationAtRealPoint(float realPointX, float realPointY){
|
||||
//// if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){
|
||||
// return terrainManager.getHeightAtPosition(realPointX, realPointY);
|
||||
//// }
|
||||
//// if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){
|
||||
//// return 0;
|
||||
//// }
|
||||
//// return 0;
|
||||
// }
|
||||
|
||||
|
||||
public boolean canValidateCell(){
|
||||
boolean rVal = false;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a cell key into its constituent coordinates in array format.
|
||||
* @param cellKey The cell key to split
|
||||
* @return The coordinates in array format
|
||||
*/
|
||||
// private int[] splitKeyToCoordinates(String cellKey){
|
||||
// int[] rVal = new int[3];
|
||||
// String[] components = cellKey.split("_");
|
||||
// for(int i = 0; i < 3; i++){
|
||||
// rVal[i] = Integer.parseInt(components[i]);
|
||||
// }
|
||||
// return rVal;
|
||||
// }
|
||||
|
||||
public boolean coordsInPhysicsSpace(int worldX, int worldY){
|
||||
return worldX <= cellX + physicsRadius && worldX >= cellX - physicsRadius && worldY <= cellY + physicsRadius && worldY >= cellY - physicsRadius;
|
||||
@ -683,38 +372,54 @@ public class DrawCellManager {
|
||||
this.generateDrawables = generate;
|
||||
}
|
||||
|
||||
boolean containsHeightmapAtDiscretePoint(int currentCellX, int currentCellY){
|
||||
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
return Globals.clientTerrainManager.containsHeightmapAtDiscretePoint(currentCellX, currentCellY);
|
||||
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float[][] getHeightmapAtPoint(int currentCellX, int currentCellY){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
return Globals.clientTerrainManager.getHeightmapAtPoint(currentCellX, currentCellY);
|
||||
}
|
||||
return Globals.serverTerrainManager.getChunk(currentCellX, currentCellY).getHeightMap();
|
||||
/**
|
||||
* Gets the chunk data at a given point
|
||||
* @param worldX The world position x component of the cell
|
||||
* @param worldY The world position y component of the cell
|
||||
* @param worldZ The world position z component of the cell
|
||||
* @return The chunk data at the specified points
|
||||
*/
|
||||
ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
||||
return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
||||
}
|
||||
|
||||
float[][] getTextureMapAtPoint(int currentCellX, int currentCellY){
|
||||
if(Globals.clientTerrainManager != null){
|
||||
return Globals.clientTerrainManager.getTextureMapAtPoint(currentCellX,currentCellY);
|
||||
} else {
|
||||
//hacky fix to +2 to this, I think the interpolation ratio was different for server/client data
|
||||
//now that we're merging/ambiguous within this class, it's out of bounds-ing unless I +2
|
||||
//TODO: investigate
|
||||
float[][] rVal = new float[commonWorldData.getDynamicInterpolationRatio() + 2][commonWorldData.getDynamicInterpolationRatio() + 2];
|
||||
rVal[1][1] = 1;
|
||||
rVal[2][1] = 1;
|
||||
rVal[3][1] = 1;
|
||||
rVal[4][1] = 1;
|
||||
rVal[5][1] = 1;
|
||||
rVal[5][2] = 1;
|
||||
rVal[6][1] = 1;
|
||||
rVal[6][2] = 1;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unique key for the cell
|
||||
* @param worldX The world position x component of the cell
|
||||
* @param worldY The world position y component of the cell
|
||||
* @param worldZ The world position z component of the cell
|
||||
* @return The key
|
||||
*/
|
||||
private String getCellKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a vector3i from the cell key
|
||||
* @param key The cell key
|
||||
* @return The vector3i containing the components of the cell key
|
||||
*/
|
||||
private Vector3i getVectorFromKey(String key){
|
||||
String[] keyComponents = key.split("_");
|
||||
return new Vector3i(Integer.parseInt(keyComponents[0]),Integer.parseInt(keyComponents[1]),Integer.parseInt(keyComponents[2]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a data cell as updateable (can be regenerated with a new model because the underlying data has changed)
|
||||
* @param chunkX The chunk x coordinate
|
||||
* @param chunkY The chunk y coordinate
|
||||
* @param chunkZ The chunk z coordinate
|
||||
*/
|
||||
public void markUpdateable(int chunkX, int chunkY, int chunkZ){
|
||||
updateable.add(getCellKey(chunkX, chunkY, chunkZ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
package electrosphere.client.terrain.editing;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
|
||||
/**
|
||||
* Utilities for editing terrain from client side of things
|
||||
*/
|
||||
public class TerrainEditing {
|
||||
|
||||
/**
|
||||
* Performs a terrain chunk edit. Basically has a sphere around the provided position that it attempts to add value to.
|
||||
* @param position The position to perform the edit
|
||||
* @param editMagnitude The magnitude of the edit to perform
|
||||
* @param type The type of block to make all edited blocks
|
||||
* @param weight The weight of the sphere to apply the edit to
|
||||
*/
|
||||
public static void editTerrain(Vector3d position, float editMagnitude, int type, float weight){
|
||||
if(position != null){
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestUseTerrainPaletteMessage(position.x, position.y, position.z, editMagnitude, weight, type));
|
||||
//calculate kernel size
|
||||
// int numPlacesToCheck = (int)((editMagnitude * 2 + 1) * (editMagnitude * 2 + 1) * (editMagnitude * 2 + 1));
|
||||
// //create and fill in kernel of positions to check
|
||||
// int[] xOffsetSet = new int[numPlacesToCheck];
|
||||
// int[] yOffsetSet = new int[numPlacesToCheck];
|
||||
// int[] zOffsetSet = new int[numPlacesToCheck];
|
||||
// int i = 0;
|
||||
// for(int x = -(int)editMagnitude; x <= (int)editMagnitude; x++){
|
||||
// for(int y = -(int)editMagnitude; y <= (int)editMagnitude; y++){
|
||||
// for(int z = -(int)editMagnitude; z <= (int)editMagnitude; z++){
|
||||
// xOffsetSet[i] = x;
|
||||
// yOffsetSet[i] = y;
|
||||
// zOffsetSet[i] = z;
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for(i = 0; i < numPlacesToCheck; i++){
|
||||
// //calculate position of edit
|
||||
// Vector3d offsetPos = new Vector3d(position).add(xOffsetSet[i],yOffsetSet[i],zOffsetSet[i]);
|
||||
// Vector3i chunkPos = Globals.clientWorldData.convertRealToChunkSpace(offsetPos);
|
||||
// Vector3i voxelPos = Globals.clientWorldData.convertRealToVoxelSpace(offsetPos);
|
||||
// //get distance from true center point of sphere to current voxel position in world space
|
||||
// float distance = (float)new Vector3d(Math.floor(offsetPos.x),Math.floor(offsetPos.y),Math.floor(offsetPos.z)).distance(position);
|
||||
// float currentPositionMagnitude = editMagnitude - distance;
|
||||
// ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkPos.x, chunkPos.y, chunkPos.z);
|
||||
// if(
|
||||
// voxelPos.x < ChunkData.CHUNK_SIZE &&
|
||||
// voxelPos.y < ChunkData.CHUNK_SIZE &&
|
||||
// voxelPos.z < ChunkData.CHUNK_SIZE &&
|
||||
// currentPositionMagnitude > 0 &&
|
||||
// data != null
|
||||
// ){
|
||||
// float current = data.getVoxelWeight()[voxelPos.x][voxelPos.y][voxelPos.z];
|
||||
// //hard clamp so it doesn't go over 1
|
||||
// float finalValue = Math.max(Math.min(current + weight,1),-1);
|
||||
// Globals.clientTerrainManager.updateChunk(
|
||||
// chunkPos.x, chunkPos.y, chunkPos.z,
|
||||
// voxelPos.x, voxelPos.y, voxelPos.z,
|
||||
// finalValue, 1
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,27 +1,38 @@
|
||||
package electrosphere.client.terrain.manager;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.cache.ClientTerrainCache;
|
||||
import electrosphere.client.terrain.cache.LoadingChunk;
|
||||
import electrosphere.client.terrain.cache.LoadingChunkCache;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Manages terrain storage and access on the client
|
||||
*/
|
||||
public class ClientTerrainManager {
|
||||
|
||||
//queues messages from server
|
||||
List<TerrainMessage> messageQueue = new CopyOnWriteArrayList<TerrainMessage>();
|
||||
|
||||
//The interpolation ratio of terrain
|
||||
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
||||
|
||||
//caches chunks from server
|
||||
static final int CACHE_SIZE = 50;
|
||||
@ -29,18 +40,17 @@ public class ClientTerrainManager {
|
||||
//used for caching the macro values
|
||||
ClientTerrainCache terrainCache;
|
||||
|
||||
//used for caching loading chunks that are still streaming over the net
|
||||
LoadingChunkCache loadingChunkCache;
|
||||
|
||||
|
||||
//The world data for the client
|
||||
ClientWorldData clientWorldData;
|
||||
|
||||
//The queue of terrain chunk data to be buffered to gpu
|
||||
static List<TerrainChunkGenQueueItem> terrainChunkGenerationQueue = new CopyOnWriteArrayList<TerrainChunkGenQueueItem>();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ClientTerrainManager(){
|
||||
terrainCache = new ClientTerrainCache(CACHE_SIZE);
|
||||
loadingChunkCache = new LoadingChunkCache();
|
||||
}
|
||||
|
||||
|
||||
@ -49,89 +59,35 @@ public class ClientTerrainManager {
|
||||
for(TerrainMessage message : messageQueue){
|
||||
messageQueue.remove(message);
|
||||
switch(message.getMessageSubtype()){
|
||||
case MACROVALUE:
|
||||
// terrainCache.addChunkValuesToCache(message.getlocationX(), message.getlocationY(), macroValues, randomizer);
|
||||
if(loadingChunkCache.containsKey(loadingChunkCache.getKey(message.getworldX(), message.getworldY()))){
|
||||
float[][] macroValues = new float[5][5];
|
||||
long[][] randomizer = new long[5][5];
|
||||
//macro values
|
||||
macroValues[0][0] = message.getmacroValue00();
|
||||
macroValues[0][1] = message.getmacroValue01();
|
||||
macroValues[0][2] = message.getmacroValue02();
|
||||
macroValues[0][3] = message.getmacroValue03();
|
||||
macroValues[0][4] = message.getmacroValue04();
|
||||
macroValues[1][0] = message.getmacroValue10();
|
||||
macroValues[1][1] = message.getmacroValue11();
|
||||
macroValues[1][2] = message.getmacroValue12();
|
||||
macroValues[1][3] = message.getmacroValue13();
|
||||
macroValues[1][4] = message.getmacroValue14();
|
||||
macroValues[2][0] = message.getmacroValue20();
|
||||
macroValues[2][1] = message.getmacroValue21();
|
||||
macroValues[2][2] = message.getmacroValue22();
|
||||
macroValues[2][3] = message.getmacroValue23();
|
||||
macroValues[2][4] = message.getmacroValue24();
|
||||
macroValues[3][0] = message.getmacroValue30();
|
||||
macroValues[3][1] = message.getmacroValue31();
|
||||
macroValues[3][2] = message.getmacroValue32();
|
||||
macroValues[3][3] = message.getmacroValue33();
|
||||
macroValues[3][4] = message.getmacroValue34();
|
||||
macroValues[4][0] = message.getmacroValue40();
|
||||
macroValues[4][1] = message.getmacroValue41();
|
||||
macroValues[4][2] = message.getmacroValue42();
|
||||
macroValues[4][3] = message.getmacroValue43();
|
||||
macroValues[4][4] = message.getmacroValue44();
|
||||
//randomizer
|
||||
randomizer[0][0] = message.getrandomizerValue00();
|
||||
randomizer[0][1] = message.getrandomizerValue01();
|
||||
randomizer[0][2] = message.getrandomizerValue02();
|
||||
randomizer[0][3] = message.getrandomizerValue03();
|
||||
randomizer[0][4] = message.getrandomizerValue04();
|
||||
randomizer[1][0] = message.getrandomizerValue10();
|
||||
randomizer[1][1] = message.getrandomizerValue11();
|
||||
randomizer[1][2] = message.getrandomizerValue12();
|
||||
randomizer[1][3] = message.getrandomizerValue13();
|
||||
randomizer[1][4] = message.getrandomizerValue14();
|
||||
randomizer[2][0] = message.getrandomizerValue20();
|
||||
randomizer[2][1] = message.getrandomizerValue21();
|
||||
randomizer[2][2] = message.getrandomizerValue22();
|
||||
randomizer[2][3] = message.getrandomizerValue23();
|
||||
randomizer[2][4] = message.getrandomizerValue24();
|
||||
randomizer[3][0] = message.getrandomizerValue30();
|
||||
randomizer[3][1] = message.getrandomizerValue31();
|
||||
randomizer[3][2] = message.getrandomizerValue32();
|
||||
randomizer[3][3] = message.getrandomizerValue33();
|
||||
randomizer[3][4] = message.getrandomizerValue34();
|
||||
randomizer[4][0] = message.getrandomizerValue40();
|
||||
randomizer[4][1] = message.getrandomizerValue41();
|
||||
randomizer[4][2] = message.getrandomizerValue42();
|
||||
randomizer[4][3] = message.getrandomizerValue43();
|
||||
randomizer[4][4] = message.getrandomizerValue44();
|
||||
LoadingChunk inProgressChunk = loadingChunkCache.fetch(loadingChunkCache.getKey(message.getworldX(), message.getworldY()));
|
||||
inProgressChunk.addMacroValues(macroValues);
|
||||
inProgressChunk.addRandomizer(randomizer);
|
||||
inProgressChunk.incrementMessageCount();
|
||||
} else {
|
||||
bouncedMessages.add(message);
|
||||
case SENDCHUNKDATA: {
|
||||
int[][][] values = new int[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE];
|
||||
float[][][] weights = new float[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
|
||||
FloatBuffer floatBuffer = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
|
||||
weights[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHUNKLOADSTART:
|
||||
if(clientWorldData != null){
|
||||
LoadingChunk newChunk = new LoadingChunk(message.getworldX(),message.getworldY(),(int)message.getvalue(),clientWorldData);
|
||||
loadingChunkCache.addLoadingChunkToCache(newChunk);
|
||||
newChunk.incrementMessageCount();
|
||||
} else {
|
||||
bouncedMessages.add(message);
|
||||
IntBuffer intView = buffer.asIntBuffer();
|
||||
intView.position(floatBuffer.position());
|
||||
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
|
||||
values[x][y][z] = intView.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HEIGHTMAPMODIFICATION:
|
||||
if(loadingChunkCache.containsKey(loadingChunkCache.getKey(message.getworldX(), message.getworldY()))){
|
||||
LoadingChunk inProgressChunk = loadingChunkCache.fetch(loadingChunkCache.getKey(message.getworldX(), message.getworldY()));
|
||||
inProgressChunk.addModification(message.getworldX(), message.getworldZ(), message.getlocationX(), message.getlocationZ(), message.getvalue());
|
||||
inProgressChunk.incrementMessageCount();
|
||||
} else {
|
||||
bouncedMessages.add(message);
|
||||
}
|
||||
break;
|
||||
ChunkData data = new ChunkData();
|
||||
data.setVoxelType(values);
|
||||
data.setVoxelWeight(weights);
|
||||
terrainCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerEngine.WARNING("ClientTerrainManager: unhandled network message of type" + message.getMessageSubtype());
|
||||
break;
|
||||
@ -146,88 +102,46 @@ public class ClientTerrainManager {
|
||||
messageQueue.add(message);
|
||||
}
|
||||
|
||||
public boolean containsHeightmapAtDiscretePoint(int x, int y){
|
||||
return terrainCache.containsHeightmapAtChunkPoint(x, y);
|
||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ);
|
||||
}
|
||||
|
||||
public boolean containsHeightmapAtRealPoint(double x, double z){
|
||||
public boolean containsChunkDataAtRealPoint(double x, double y, double z){
|
||||
assert clientWorldData != null;
|
||||
return terrainCache.containsHeightmapAtChunkPoint(clientWorldData.convertRealToChunkSpace(x), clientWorldData.convertRealToChunkSpace(z));
|
||||
return terrainCache.containsChunkDataAtWorldPoint(
|
||||
clientWorldData.convertRealToChunkSpace(x),
|
||||
clientWorldData.convertRealToChunkSpace(y),
|
||||
clientWorldData.convertRealToChunkSpace(z)
|
||||
);
|
||||
}
|
||||
|
||||
public float[][] getHeightmapAtPoint(int x, int y){
|
||||
return terrainCache.getHeightmapAtChunkPoint(x, y);
|
||||
}
|
||||
|
||||
|
||||
public double getHeightAtPosition(double x, double y){
|
||||
assert clientWorldData != null;
|
||||
//get chunk coordinate space of input x,y
|
||||
int chunkX = (int)Math.floor(x / clientWorldData.getDynamicInterpolationRatio());
|
||||
int chunkY = (int)Math.floor(y / clientWorldData.getDynamicInterpolationRatio());
|
||||
//get local coordinate space of input x,y
|
||||
double localX = x - chunkX * clientWorldData.getDynamicInterpolationRatio();
|
||||
double localY = y - chunkY * clientWorldData.getDynamicInterpolationRatio();
|
||||
//get chunk elevation map
|
||||
float[][] chunkElevationMap = getHeightmapAtPoint(chunkX,chunkY);
|
||||
//floored variants of local values
|
||||
int localXf = (int)Math.floor(localX);
|
||||
int localYf = (int)Math.floor(localY);
|
||||
|
||||
/*
|
||||
Average some inner value.
|
||||
|
||||
01 11
|
||||
0.3 0.4 0.5
|
||||
0.1 0.2 0.3
|
||||
00 10
|
||||
*/
|
||||
//interp elevation from map
|
||||
float elevation00 = chunkElevationMap[(int)localX+0][(int)localY+0];
|
||||
float elevation10 = chunkElevationMap[(int)localX+1][(int)localY+0];
|
||||
float elevation01 = chunkElevationMap[(int)localX+0][(int)localY+1];
|
||||
float elevation11 = chunkElevationMap[(int)localX+1][(int)localY+1];
|
||||
|
||||
double rVal =
|
||||
(1-(localX-localXf))*(1-(localY-localYf)) * elevation00 +
|
||||
( (localX-localXf))*(1-(localY-localYf)) * elevation10 +
|
||||
(1-(localX-localXf))*( (localY-localYf)) * elevation01 +
|
||||
( (localX-localXf))*( (localY-localYf)) * elevation11
|
||||
;
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public float[][] getTextureMapAtPoint(int x, int y){
|
||||
assert clientWorldData != null;
|
||||
float[][] rVal = new float[clientWorldData.getDynamicInterpolationRatio()][clientWorldData.getDynamicInterpolationRatio()];
|
||||
rVal[1][1] = 1;
|
||||
rVal[2][1] = 1;
|
||||
rVal[3][1] = 1;
|
||||
rVal[4][1] = 1;
|
||||
rVal[5][1] = 1;
|
||||
rVal[5][2] = 1;
|
||||
rVal[6][1] = 1;
|
||||
rVal[6][2] = 1;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public void ejectLoadedChunks(){
|
||||
if(clientWorldData != null){
|
||||
List<LoadingChunk> chunksToEject = new LinkedList<LoadingChunk>();
|
||||
for(LoadingChunk chunk : loadingChunkCache.getChunks()){
|
||||
if(chunk.isComplete() && chunk.getClientWorldData() != null){
|
||||
float[][] heightMap = chunk.exportFloats();
|
||||
terrainCache.addFloatsToCache(chunk.getWorldX(), chunk.getWorldY(), heightMap);
|
||||
chunksToEject.add(chunk);
|
||||
}
|
||||
}
|
||||
for(LoadingChunk loadedChunk : chunksToEject){
|
||||
loadingChunkCache.remove(loadedChunk);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the chunk data at a given world position
|
||||
* @param worldX The x component of the world coordinate
|
||||
* @param worldY The y component of the world coordinate
|
||||
* @param worldZ The z component of the world coordinate
|
||||
* @return The chunk data if it exists, otherwise null
|
||||
*/
|
||||
public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||
return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk data at a given world position
|
||||
* @param worldPos The world position as a joml vector
|
||||
* @return The chunk data if it exists, otherwise null
|
||||
*/
|
||||
public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos){
|
||||
return terrainCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Queues a terrain chunk to be pushed to GPU based on chunk data
|
||||
* @param data The chunk data (triangles, normals, etc)
|
||||
* @return The model path that is promised to eventually reflect the terrain model when it makes it to gpu
|
||||
*/
|
||||
public static String queueTerrainGridGeneration(TerrainChunkData data){
|
||||
String promisedHash = "";
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
@ -237,6 +151,9 @@ public class ClientTerrainManager {
|
||||
return promisedHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes all terrain data in queue to the gpu and registers the resulting models
|
||||
*/
|
||||
public static void generateTerrainChunkGeometry(){
|
||||
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
|
||||
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData());
|
||||
@ -246,12 +163,20 @@ public class ClientTerrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client world data that this terrain manager references
|
||||
* @param clientWorldData The client world data
|
||||
* Gets all chunks in the terrain cache
|
||||
* @return The collection of all chunk data objects
|
||||
*/
|
||||
public void setClientWorldData(ClientWorldData clientWorldData){
|
||||
this.clientWorldData = clientWorldData;
|
||||
this.terrainCache.setClientWorldData(clientWorldData);
|
||||
public Collection<ChunkData> getAllChunks(){
|
||||
return terrainCache.getAllChunks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the world position of a given chunk
|
||||
* @param chunk The chunk
|
||||
* @return The world position of the chunk
|
||||
*/
|
||||
public Vector3i getPositionOfChunk(ChunkData chunk){
|
||||
return terrainCache.getChunkPosition(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
718
src/main/java/electrosphere/collision/CollisionEngine.java
Normal file
@ -0,0 +1,718 @@
|
||||
package electrosphere.collision;
|
||||
|
||||
import static org.ode4j.ode.OdeConstants.dContactBounce;
|
||||
import static org.ode4j.ode.OdeConstants.dContactSoftCFM;
|
||||
import static org.ode4j.ode.OdeConstants.dInfinity;
|
||||
import static org.ode4j.ode.OdeHelper.areConnectedExcluding;
|
||||
import static org.ode4j.ode.OdeMath.dCalcVectorLengthSquare3;
|
||||
import static org.ode4j.ode.OdeMath.dSubtractVectors3;
|
||||
import static org.ode4j.ode.internal.Common.dRecip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.ode4j.math.DMatrix3;
|
||||
import org.ode4j.math.DVector3;
|
||||
import org.ode4j.math.DVector4;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DBox;
|
||||
import org.ode4j.ode.DCapsule;
|
||||
import org.ode4j.ode.DContact;
|
||||
import org.ode4j.ode.DContactBuffer;
|
||||
import org.ode4j.ode.DContactJoint;
|
||||
import org.ode4j.ode.DCylinder;
|
||||
import org.ode4j.ode.DGeom;
|
||||
import org.ode4j.ode.DGeom.DNearCallback;
|
||||
import org.ode4j.ode.DJoint;
|
||||
import org.ode4j.ode.DJointGroup;
|
||||
import org.ode4j.ode.DMass;
|
||||
import org.ode4j.ode.DRay;
|
||||
import org.ode4j.ode.DSpace;
|
||||
import org.ode4j.ode.DSphere;
|
||||
import org.ode4j.ode.DTriMesh;
|
||||
import org.ode4j.ode.DTriMeshData;
|
||||
import org.ode4j.ode.DWorld;
|
||||
import org.ode4j.ode.OdeHelper;
|
||||
|
||||
import electrosphere.collision.RayCastCallback.RayCastCallbackData;
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.state.collidable.Impulse;
|
||||
import electrosphere.entity.types.hitbox.HitboxData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
|
||||
/**
|
||||
* The main collision engine class. Tracks all entities that collide in its system and fires callbacks when they do.
|
||||
*/
|
||||
public class CollisionEngine {
|
||||
|
||||
public static final float ENGINE_STEP_SIZE = 1000.0f / 120.0f;
|
||||
|
||||
//world data that the collision engine leverages for position correction and the like
|
||||
CollisionWorldData collisionWorldData;
|
||||
|
||||
private DWorld world;
|
||||
private DSpace space;
|
||||
private Semaphore spaceLock = new Semaphore(1);
|
||||
private DJointGroup contactgroup;
|
||||
|
||||
private static final int MAX_CONTACTS = 1; // maximum number of contact points per body
|
||||
|
||||
List<Entity> collisionEntities = new ArrayList<Entity>();
|
||||
List<Entity> physicsEntities = new ArrayList<Entity>();
|
||||
List<Entity> dynamicPhysicsEntities = new ArrayList<Entity>();
|
||||
List<Entity> structurePhysicsEntities = new ArrayList<Entity>();
|
||||
List<DBody> bodies = new ArrayList<DBody>();
|
||||
Map<DBody,Collidable> bodyPointerMap = new HashMap<DBody,Collidable>();
|
||||
List<Collidable> collidableList = new ArrayList<Collidable>();
|
||||
|
||||
//callbacks for collision check
|
||||
RayCastCallback rayCastCallback = new RayCastCallback();
|
||||
|
||||
static final float linearDamping = 0.02f;
|
||||
|
||||
public CollisionEngine(){
|
||||
world = OdeHelper.createWorld();
|
||||
space = OdeHelper.createHashSpace();
|
||||
contactgroup = OdeHelper.createJointGroup();
|
||||
}
|
||||
|
||||
|
||||
public static void resolveCollision(Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude){
|
||||
switch(receiver.getType()){
|
||||
case Collidable.TYPE_CREATURE:
|
||||
switch(impactor.getType()){
|
||||
case Collidable.TYPE_TERRAIN:
|
||||
// System.out.println(EntityUtils.getPosition(impactor.getParent()) + " " + EntityUtils.getPosition(receiver.getParent()));
|
||||
// System.out.println();
|
||||
// System.out.println("Terrain-creature collision: " + normal + " mag:" + magnitude);
|
||||
// if(normal.y > normal.x + normal.z){
|
||||
// normal.x = 0;
|
||||
// normal.z = 0;
|
||||
// }
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_TERRAIN));
|
||||
break;
|
||||
case Collidable.TYPE_CREATURE:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE));
|
||||
break;
|
||||
case Collidable.TYPE_STRUCTURE:
|
||||
// float realMag = 1f/(float)Math.pow(0.1, magnitude);
|
||||
// System.out.println(normal + " - " + realMag);
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_STRUCTURE));
|
||||
// System.out.println("Structure-creature collision");
|
||||
break;
|
||||
case Collidable.TYPE_ITEM:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_ITEM));
|
||||
break;
|
||||
case Collidable.TYPE_OBJECT:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Collidable.TYPE_ITEM:
|
||||
switch(impactor.getType()){
|
||||
case Collidable.TYPE_TERRAIN:
|
||||
// System.out.println(EntityUtils.getPosition(impactor.getParent()) + " " + EntityUtils.getPosition(receiver.getParent()));
|
||||
// System.out.println();
|
||||
// System.out.println("Terrain-item collision: " + normal + " mag:" + magnitude);
|
||||
// if(normal.y > normal.x + normal.z){
|
||||
// normal.x = 0;
|
||||
// normal.z = 0;
|
||||
// }
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_TERRAIN));
|
||||
break;
|
||||
case Collidable.TYPE_CREATURE:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE));
|
||||
break;
|
||||
case Collidable.TYPE_STRUCTURE:
|
||||
// float realMag = 1f/(float)Math.pow(0.1, magnitude);
|
||||
// System.out.println(normal + " - " + realMag);
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_STRUCTURE));
|
||||
// System.out.println("Structure-creature collision");
|
||||
break;
|
||||
case Collidable.TYPE_ITEM:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_ITEM));
|
||||
break;
|
||||
case Collidable.TYPE_OBJECT:
|
||||
receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCollidableImpulseLists(){
|
||||
for(Collidable collidable : collidableList){
|
||||
collidable.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param e the entity that wants to move
|
||||
* @param positionToCheck the position that it wants to move to
|
||||
* @return true if it can occupy that position, false otherwise
|
||||
*/
|
||||
public boolean checkCanOccupyPosition(CollisionWorldData w, Entity e, Vector3d positionToCheck){
|
||||
boolean rVal = true;
|
||||
//
|
||||
// check world bounds
|
||||
//
|
||||
if(
|
||||
positionToCheck.x < collisionWorldData.getWorldBoundMin().x ||
|
||||
positionToCheck.z < collisionWorldData.getWorldBoundMin().z ||
|
||||
positionToCheck.x > collisionWorldData.getWorldBoundMax().x ||
|
||||
positionToCheck.z > collisionWorldData.getWorldBoundMax().z
|
||||
){
|
||||
return false;
|
||||
}
|
||||
// //
|
||||
// // are we below the terrain?
|
||||
// //
|
||||
// if(w.getElevationAtPoint(positionToCheck) > positionToCheck.y){
|
||||
// return false;
|
||||
// }
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the collision and simulation phases for this collision engine
|
||||
* @param time The time to increment the physics simulation by
|
||||
*/
|
||||
public void simulatePhysics(float time){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
space.collide(0,nearCallback);
|
||||
// space.collide2(space, collisionWorldData, nearCallback);
|
||||
// world.quickStep(ENGINE_STEP_SIZE);
|
||||
|
||||
// remove all contact joints
|
||||
contactgroup.empty();
|
||||
spaceLock.release();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for any near collisions in the broadphase of the collision check
|
||||
*/
|
||||
private DNearCallback nearCallback = new DNearCallback() {
|
||||
@Override
|
||||
public void call(Object data, DGeom o1, DGeom o2) {
|
||||
nearCallback( data, o1, o2);
|
||||
}
|
||||
};
|
||||
|
||||
//buffer for collisions
|
||||
DContactBuffer contacts = null;
|
||||
|
||||
// this is called by dSpaceCollide when two objects in space are
|
||||
// potentially colliding.
|
||||
private void nearCallback (Object data, DGeom o1, DGeom o2) {
|
||||
// if (o1->body && o2->body) return;
|
||||
|
||||
// exit without doing anything if the two bodies are connected by a joint
|
||||
DBody b1 = o1.getBody();
|
||||
DBody b2 = o2.getBody();
|
||||
if (b1!=null && b2!=null && areConnectedExcluding (b1,b2,DContactJoint.class)) return;
|
||||
|
||||
//creates a buffer to store potential collisions
|
||||
DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box
|
||||
for (int i=0; i<MAX_CONTACTS; i++) {
|
||||
DContact contact = contacts.get(i);
|
||||
contact.surface.mode = dContactBounce | dContactSoftCFM;
|
||||
contact.surface.mu = dInfinity;
|
||||
contact.surface.mu2 = 0;
|
||||
contact.surface.bounce = 0.1;
|
||||
contact.surface.bounce_vel = 0.1;
|
||||
contact.surface.soft_cfm = 0.01;
|
||||
}
|
||||
if(
|
||||
bodyPointerMap.get(b1) != null &&
|
||||
bodyPointerMap.get(b2) != null &&
|
||||
!(bodyPointerMap.get(b1).getType() == Collidable.TYPE_TERRAIN &&
|
||||
bodyPointerMap.get(b2).getType() == Collidable.TYPE_TERRAIN)){
|
||||
//calculate collisions
|
||||
int numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer());
|
||||
//create DContacts based on each collision that occurs
|
||||
if (numc != 0) {
|
||||
DMatrix3 RI = new DMatrix3();
|
||||
RI.setIdentity ();
|
||||
for (int i=0; i<numc; i++) {
|
||||
DContact contact = contacts.get(i);
|
||||
//special code for ray casting
|
||||
if (o1 instanceof DRay || o2 instanceof DRay){
|
||||
DMatrix3 Rotation = new DMatrix3();
|
||||
Rotation.setIdentity();
|
||||
// dsDrawSphere(contact.geom.pos, Rotation, (0.01));
|
||||
|
||||
DVector3 End = new DVector3();
|
||||
End.eqSum( contact.geom.pos, contact.geom.normal, contact.geom.depth );
|
||||
|
||||
// dsDrawLine(contact.geom.pos, End);
|
||||
continue;
|
||||
}
|
||||
// contact.geom.pos
|
||||
resolveCollision(
|
||||
bodyPointerMap.get(o1.getBody()),
|
||||
bodyPointerMap.get(o2.getBody()),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.geom.normal).mul(-1.0),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.fdir1).mul(-1.0),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
|
||||
(float)contact.geom.depth
|
||||
);
|
||||
resolveCollision(
|
||||
bodyPointerMap.get(o2.getBody()),
|
||||
bodyPointerMap.get(o1.getBody()),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.geom.normal),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.fdir1),
|
||||
PhysicsUtils.odeVecToJomlVec(contact.geom.pos),
|
||||
(float)contact.geom.depth
|
||||
);
|
||||
|
||||
//add contact to contact group
|
||||
DJoint c = OdeHelper.createContactJoint (world,contactgroup,contact );
|
||||
c.attach (b1,b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param e the entity that's trying to move
|
||||
* @param positionToCheck the position the entity wants to be at
|
||||
* @return the position the engine recommends it move to instead (this is
|
||||
* guaranteed to be a valid position)
|
||||
*/
|
||||
public Vector3d suggestMovementPosition(CollisionWorldData w, Entity e, Vector3d positionToCheck){
|
||||
Vector3d suggestedPosition = new Vector3d(positionToCheck);
|
||||
//
|
||||
// adjust for minimum height (Terrain)
|
||||
//
|
||||
// float heightMapBias = 0.00001f;
|
||||
// if(w.getElevationAtPoint(positionToCheck) > positionToCheck.y){
|
||||
// suggestedPosition.y = w.getElevationAtPoint(positionToCheck) + heightMapBias;
|
||||
// }
|
||||
//
|
||||
// adjust for world bounds
|
||||
//
|
||||
if(suggestedPosition.x < collisionWorldData.getWorldBoundMin().x){
|
||||
suggestedPosition.x = collisionWorldData.getWorldBoundMin().x;
|
||||
}
|
||||
if(suggestedPosition.z < collisionWorldData.getWorldBoundMin().z){
|
||||
suggestedPosition.z = collisionWorldData.getWorldBoundMin().z;
|
||||
}
|
||||
if(suggestedPosition.x > collisionWorldData.getWorldBoundMax().x){
|
||||
suggestedPosition.x = collisionWorldData.getWorldBoundMax().x;
|
||||
}
|
||||
if(suggestedPosition.z > collisionWorldData.getWorldBoundMax().z){
|
||||
suggestedPosition.z = collisionWorldData.getWorldBoundMax().z;
|
||||
}
|
||||
return suggestedPosition;
|
||||
}
|
||||
|
||||
|
||||
public void registerCollidableEntity(Entity collidable){
|
||||
collisionEntities.add(collidable);
|
||||
}
|
||||
|
||||
public List<Entity> getCollisionEntities(){
|
||||
return collisionEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collision world data
|
||||
* @param collisionWorldData The collision world data
|
||||
*/
|
||||
public void setCollisionWorldData(CollisionWorldData collisionWorldData){
|
||||
this.collisionWorldData = collisionWorldData;
|
||||
}
|
||||
|
||||
|
||||
public boolean collisionSphereCheck(Entity hitbox1, HitboxData hitbox1data, Entity hitbox2, HitboxData hitbox2data){
|
||||
Vector3d position1 = EntityUtils.getPosition(hitbox1);
|
||||
Vector3d position2 = EntityUtils.getPosition(hitbox2);
|
||||
float radius1 = hitbox1data.getRadius();
|
||||
float radius2 = hitbox2data.getRadius();
|
||||
double distance = position1.distance(position2);
|
||||
if(distance < radius1 + radius2){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerPhysicsEntity(Entity physicsEntity){
|
||||
physicsEntities.add(physicsEntity);
|
||||
}
|
||||
|
||||
public List<Entity> getPhysicsEntities(){
|
||||
return physicsEntities;
|
||||
}
|
||||
|
||||
public void deregisterPhysicsEntity(Entity physicsEntity){
|
||||
physicsEntities.remove(physicsEntity);
|
||||
}
|
||||
|
||||
public void registerDynamicPhysicsEntity(Entity dynamicEntity){
|
||||
dynamicPhysicsEntities.add(dynamicEntity);
|
||||
}
|
||||
|
||||
public void deregisterDynamicPhysicsEntity(Entity dynamicEntity){
|
||||
dynamicPhysicsEntities.remove(dynamicEntity);
|
||||
}
|
||||
|
||||
public List<Entity> getDynamicPhysicsEntities(){
|
||||
return dynamicPhysicsEntities;
|
||||
}
|
||||
|
||||
public void registerStructurePhysicsEntity(Entity structureEntity){
|
||||
structurePhysicsEntities.add(structureEntity);
|
||||
}
|
||||
|
||||
public List<Entity> getStructurePhysicsEntities(){
|
||||
return structurePhysicsEntities;
|
||||
}
|
||||
|
||||
public void updateDynamicObjectTransforms(){
|
||||
for(Entity dynamicEntity : dynamicPhysicsEntities){
|
||||
DBody rigidBody = (DBody)dynamicEntity.getData(EntityDataStrings.PHYSICS_COLLISION_BODY);
|
||||
Vector3d offset = (Vector3d)dynamicEntity.getData(EntityDataStrings.PHYSICS_COLLISION_BODY_OFFSET);
|
||||
Vector3d newPosition = PhysicsUtils.getRigidBodyPosition(rigidBody).sub(offset);
|
||||
Quaterniond newRotation = PhysicsUtils.getRigidBodyRotation(rigidBody);
|
||||
// System.out.println(rigidBody + " position " + newPosition);
|
||||
// System.out.println("Linear velocity: " + rigidBody.getLinearVelocity(new javax.vecmath.Vector3f()));
|
||||
EntityUtils.getPosition(dynamicEntity).set(newPosition);
|
||||
EntityUtils.getRotation(dynamicEntity).set(newRotation);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerCollisionObject(DBody body, Collidable collidable){
|
||||
registerPhysicsObject(body);
|
||||
bodyPointerMap.put(body,collidable);
|
||||
collidableList.add(collidable);
|
||||
}
|
||||
|
||||
public void listBodyPositions(){
|
||||
for(DBody body : bodies){
|
||||
LoggerInterface.loggerEngine.INFO("" + body);
|
||||
LoggerInterface.loggerEngine.INFO("" + PhysicsUtils.getRigidBodyPosition(body));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the entity is being accelerated by gravity
|
||||
*/
|
||||
// public boolean gravityCheck(CommonWorldData w, Entity e){
|
||||
// double worldHeight = w.getElevationAtPoint(EntityUtils.getPosition(e));
|
||||
// double entityHeight = EntityUtils.getPosition(e).y;
|
||||
// return entityHeight > worldHeight + 0.1f;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Casts a ray into the scene and returns the first entity that the ray collides with.
|
||||
* This will collide with any type of collidable object.
|
||||
* @param start THe start position of the ray
|
||||
* @param direction The direction the ray will travel in
|
||||
* @param length The length of the ray to cast
|
||||
* @return The entity that the ray collides with if successful, null otherwise
|
||||
*/
|
||||
public Entity rayCast(Vector3d start, Vector3d direction, double length){
|
||||
return rayCast(start,direction,length,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a ray into the collision space and returns the first entity that the ray collides with.
|
||||
* The type mask is a list of collidable types that are valid collisions.
|
||||
* For instance, if the typeMask only contains Collidable.TYPE_TERRAIN, only entities with the type terrain will
|
||||
* be returned from the raycast.
|
||||
* @param start The start position of the way
|
||||
* @param length The length to cast the ray out to
|
||||
* @param typeMask The mask of types to collide the ray with
|
||||
* @return The entity that the ray cast collided with. Will be null if no entity was collided with.
|
||||
*/
|
||||
public Entity rayCast(Vector3d start, Vector3d direction, double length, List<String> typeMask){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
//create the ray
|
||||
DRay ray = OdeHelper.createRay(space, length);
|
||||
ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z);
|
||||
//collide
|
||||
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask);
|
||||
rayCastCallback.setLength(length);
|
||||
space.collide2(space, data, rayCastCallback);
|
||||
//destroy ray
|
||||
ray.destroy();
|
||||
spaceLock.release();
|
||||
return data.collidedEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ray casts into the scene and gets the position of the closest collision's position in world space.
|
||||
* Will collide with any collidable types including characters and items.
|
||||
* @param start The start position of the ray to cast
|
||||
* @param direction The direction of the ray to cast
|
||||
* @param length The length of the ray to cast
|
||||
* @return The position, in world coordinates, of the closest collision of the way, or null if it did not collide with anything.
|
||||
*/
|
||||
public Vector3d rayCastPosition(Vector3d start, Vector3d direction, double length){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
//create the ray
|
||||
DRay ray = OdeHelper.createRay(space, length);
|
||||
ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z);
|
||||
//collide
|
||||
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, null);
|
||||
rayCastCallback.setLength(length);
|
||||
space.collide2(ray, data, rayCastCallback);
|
||||
//destroy ray
|
||||
ray.destroy();
|
||||
spaceLock.release();
|
||||
return data.collisionPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ray casts into the scene and gets the position of the closest collision's position in world space.
|
||||
* @param start The start position of the ray to cast
|
||||
* @param direction The direction of the ray to cast
|
||||
* @param length The length of the ray to cast
|
||||
* @param typeMask The mask of types to collide the ray with
|
||||
* @return The position, in world coordinates, of the closest collision of the way, or null if it did not collide with anything.
|
||||
*/
|
||||
public Vector3d rayCastPositionMasked(Vector3d start, Vector3d direction, double length, List<String> typeMask){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
//create the ray
|
||||
DRay ray = OdeHelper.createRay(space, length);
|
||||
ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z);
|
||||
//collide
|
||||
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask);
|
||||
rayCastCallback.setLength(length);
|
||||
space.collide2(ray, data, rayCastCallback);
|
||||
//destroy ray
|
||||
ray.destroy();
|
||||
spaceLock.release();
|
||||
return data.collisionPosition;
|
||||
}
|
||||
|
||||
public void registerPhysicsObject(DBody body){
|
||||
if(!bodies.contains(body)){
|
||||
bodies.add(body);
|
||||
// OdeHelper.createBody(world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a body and all geometry under the body
|
||||
* @param body The DBody to destroy
|
||||
*/
|
||||
public void deregisterPhysicsObject(DBody body){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
if(bodies.contains(body)){
|
||||
bodies.remove(body);
|
||||
}
|
||||
Iterator<DGeom> geomIterator = body.getGeomIterator();
|
||||
while(geomIterator.hasNext()){
|
||||
DGeom geom = geomIterator.next();
|
||||
space.remove(geom);
|
||||
geom.destroy();
|
||||
}
|
||||
body.destroy();
|
||||
spaceLock.release();
|
||||
}
|
||||
|
||||
public void deregisterRigidBody(DBody body){
|
||||
if(bodies.contains(body)){
|
||||
bodies.remove(body);
|
||||
}
|
||||
if((body) != null){
|
||||
body.destroy();
|
||||
// world.removeRigidBody(body);
|
||||
}
|
||||
}
|
||||
|
||||
public void deregisterCollidableEntity(Entity e){
|
||||
if(collisionEntities.contains(e)){
|
||||
collisionEntities.remove(e);
|
||||
}
|
||||
if(physicsEntities.contains(e)){
|
||||
physicsEntities.remove(e);
|
||||
}
|
||||
if(dynamicPhysicsEntities.contains(e)){
|
||||
dynamicPhysicsEntities.remove(e);
|
||||
}
|
||||
if(structurePhysicsEntities.contains(e)){
|
||||
structurePhysicsEntities.remove(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroyEntityThatHasPhysics(Entity e){
|
||||
//make uncollidable
|
||||
if(e.containsKey(EntityDataStrings.PHYSICS_COLLISION_BODY)){
|
||||
DBody rigidBody = (DBody)e.getData(EntityDataStrings.PHYSICS_COLLISION_BODY);
|
||||
deregisterPhysicsObject(rigidBody);
|
||||
}
|
||||
deregisterCollidableEntity(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ode DSpace
|
||||
* @return The DSpace
|
||||
*/
|
||||
public DSpace getSpace(){
|
||||
return space;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DWorld for this collision engine
|
||||
* @return The DWorld
|
||||
*/
|
||||
public DWorld getDWorld(){
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a trimesh from a given set of vertices and indices
|
||||
* @param verts The vertices
|
||||
* @param indices The indices
|
||||
* @return The DTriMesh
|
||||
*/
|
||||
protected DTriMesh createTrimeshGeom(float[] verts, int[] indices){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DTriMeshData data = OdeHelper.createTriMeshData();
|
||||
data.build(verts, indices);
|
||||
DTriMesh rVal = OdeHelper.createTriMesh(getSpace(), data);
|
||||
spaceLock.release();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cube geometry. Dimensions vector control the total length of the x, y, and z dimensions respectively.
|
||||
* @param dimensions The dimensions of the box
|
||||
* @return The DBox
|
||||
*/
|
||||
protected DBox createCubeGeom(Vector3d dimensions){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DBox boxGeom = OdeHelper.createBox(space, dimensions.x, dimensions.y, dimensions.z);
|
||||
spaceLock.release();
|
||||
return boxGeom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cylinder geometry in the physics space
|
||||
* @param dimensions The dimensions of the cylinder. X is the radius, y is the total height.
|
||||
* @return The cylinder geometry
|
||||
*/
|
||||
protected DCylinder createCylinderGeom(double radius, double length){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DCylinder cylinderGeom = OdeHelper.createCylinder(space, radius, length);
|
||||
spaceLock.release();
|
||||
return cylinderGeom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a sphere geometry in the physics space
|
||||
* @param radius The radius of the sphere
|
||||
* @return The sphere geometry
|
||||
*/
|
||||
protected DSphere createSphereGeom(double radius){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DSphere sphereGeom = OdeHelper.createSphere(space, radius);
|
||||
spaceLock.release();
|
||||
return sphereGeom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a capsule geometry in the physics space
|
||||
* @param radius The radius of the capsule
|
||||
* @param length The length of the capsule
|
||||
* @return The capsule geometry
|
||||
*/
|
||||
protected DCapsule createCapsuleGeom(double radius, double length){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DCapsule capsuleGeom = OdeHelper.createCapsule(space, radius, length);
|
||||
spaceLock.release();
|
||||
return capsuleGeom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DBody. Can optionally be passed DGeom objects to be attached to the body when it is created.
|
||||
* @param geom The geometry objects to attach to the body on creation
|
||||
* @return The DBody
|
||||
*/
|
||||
protected DBody createDBody(DGeom ...geom){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DBody body = OdeHelper.createBody(world);
|
||||
if(geom != null){
|
||||
for(int i = 0; i < geom.length; i++){
|
||||
if(geom != null){
|
||||
geom[i].setBody(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
spaceLock.release();
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DMass and attaches a body to it
|
||||
* @param massValue The amount of mass for the object
|
||||
* @param body The DBody to attach the mass to
|
||||
* @return The DMass
|
||||
*/
|
||||
protected DMass createDMass(double massValue, DBody body){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
DMass mass = OdeHelper.createMass();
|
||||
body.setMass(mass);
|
||||
spaceLock.release();
|
||||
return mass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transform on a body
|
||||
* @param body The body
|
||||
* @param position The position
|
||||
* @param rotation The rotation
|
||||
*/
|
||||
protected void setBodyTransform(DBody body, Vector3d position, Quaterniond rotation){
|
||||
spaceLock.acquireUninterruptibly();
|
||||
body.setPosition(position.x, position.y, position.z);
|
||||
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
|
||||
spaceLock.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the body in a thread-safe way
|
||||
* @param body The body to get the position of
|
||||
* @return The position
|
||||
*/
|
||||
protected Vector3d getBodyPosition(DBody body){
|
||||
Vector3d rVal = null;
|
||||
spaceLock.acquireUninterruptibly();
|
||||
rVal = PhysicsUtils.odeVecToJomlVec(body.getPosition());
|
||||
spaceLock.release();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rotation of the body in a thread-safe way
|
||||
* @param body The body to get the rotation of
|
||||
* @return The rotation
|
||||
*/
|
||||
protected Quaterniond getBodyRotation(DBody body){
|
||||
Quaterniond rVal = null;
|
||||
spaceLock.acquireUninterruptibly();
|
||||
rVal = PhysicsUtils.odeQuatToJomlQuat(body.getQuaternion());
|
||||
spaceLock.release();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package electrosphere.game.collision;
|
||||
package electrosphere.collision;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
@ -8,7 +8,7 @@ import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public class CommonWorldData {
|
||||
public class CollisionWorldData {
|
||||
|
||||
|
||||
|
||||
@ -20,17 +20,12 @@ public class CommonWorldData {
|
||||
ServerWorldData serverWorldData;
|
||||
ServerTerrainManager serverTerrainManager;
|
||||
|
||||
|
||||
|
||||
public CommonWorldData(ClientWorldData clientWorldData, ClientTerrainManager clientTerrainManager){
|
||||
this.clientWorldData = clientWorldData;
|
||||
this.clientTerrainManager = clientTerrainManager;
|
||||
}
|
||||
|
||||
|
||||
public CommonWorldData(ServerWorldData serverWorldData, ServerTerrainManager serverTerrainManager){
|
||||
public CollisionWorldData(ServerWorldData serverWorldData){
|
||||
this.serverWorldData = serverWorldData;
|
||||
this.serverTerrainManager = serverTerrainManager;
|
||||
}
|
||||
|
||||
public CollisionWorldData(ClientWorldData clientWorldData){
|
||||
this.clientWorldData = clientWorldData;
|
||||
}
|
||||
|
||||
|
||||
@ -44,17 +39,17 @@ public class CommonWorldData {
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public double getElevationAtPoint(Vector3d position){
|
||||
if(clientWorldData != null){
|
||||
if(clientTerrainManager.containsHeightmapAtRealPoint(position.x, position.z)){
|
||||
return clientTerrainManager.getHeightAtPosition(position.x, position.z);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return serverTerrainManager.getHeightAtPosition(position.x, position.z);
|
||||
}
|
||||
}
|
||||
// public double getElevationAtPoint(Vector3d position){
|
||||
// if(clientWorldData != null){
|
||||
// if(clientTerrainManager.containsHeightmapAtRealPoint(position.x, position.z)){
|
||||
// return clientTerrainManager.getHeightAtPosition(position.x, position.z);
|
||||
// } else {
|
||||
// return 0;
|
||||
// }
|
||||
// } else {
|
||||
// return serverTerrainManager.getHeightAtPosition(position.x, position.z);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public Vector3f getWorldBoundMin(){
|
||||
353
src/main/java/electrosphere/collision/PhysicsUtils.java
Normal file
@ -0,0 +1,353 @@
|
||||
package electrosphere.collision;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.assimp.AIFace;
|
||||
import org.lwjgl.assimp.AIMesh;
|
||||
import org.lwjgl.assimp.AIScene;
|
||||
import org.lwjgl.assimp.AIVector3D;
|
||||
import org.ode4j.math.DQuaternion;
|
||||
import org.ode4j.math.DQuaternionC;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DBox;
|
||||
import org.ode4j.ode.DCylinder;
|
||||
import org.ode4j.ode.DMass;
|
||||
import org.ode4j.ode.DPlane;
|
||||
import org.ode4j.ode.DSphere;
|
||||
import org.ode4j.ode.DTriMesh;
|
||||
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
|
||||
/**
|
||||
* Utilities for leveraging the collision system to perform physics
|
||||
*/
|
||||
public class PhysicsUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Attaches a heightmap dbody to an entity
|
||||
* @param terrain The terrain entity
|
||||
* @param heightfield The heightfield values
|
||||
* @return The DBody created
|
||||
*/
|
||||
public static DBody clientAttachTerrainRigidBody(Entity terrain, CollisionEngine collisionEngine, float[][] heightfield){
|
||||
|
||||
Vector3d position = EntityUtils.getPosition(terrain);
|
||||
|
||||
int arrayLength = heightfield.length;
|
||||
int arrayWidth = heightfield[0].length;
|
||||
|
||||
float collisionMargin = 0.08f;
|
||||
|
||||
/*
|
||||
Traditional buffer code not working for some reason
|
||||
the approach of
|
||||
https://stackoverflow.com/questions/40855945/lwjgl-mesh-to-jbullet-collider
|
||||
works much better
|
||||
IDK why
|
||||
*/
|
||||
|
||||
int numberTriangles = (arrayLength - 1) * (arrayWidth - 1) * 2;
|
||||
int triangleStride = 0;
|
||||
|
||||
int numberVertices = arrayLength * arrayWidth;
|
||||
int vertexStride = 0;
|
||||
|
||||
float[] vertices = new float[numberVertices * 3];
|
||||
int vertexInserterPos = 0;
|
||||
int[] indices = new int[numberTriangles * 3];
|
||||
int indexInserterPos = 0;
|
||||
|
||||
for(int x = 0; x < arrayLength; x++){
|
||||
for(int y = 0; y < arrayWidth; y++){
|
||||
vertices[vertexInserterPos] = x;
|
||||
vertexInserterPos++;
|
||||
vertices[vertexInserterPos] = heightfield[x][y] - collisionMargin;
|
||||
vertexInserterPos++;
|
||||
vertices[vertexInserterPos] = y;
|
||||
vertexInserterPos++;
|
||||
if(x < arrayLength - 1 && y < arrayWidth - 1){
|
||||
//if we should also add a triangle index
|
||||
/*
|
||||
as copied from ModelUtil's terrain mesh generation function
|
||||
faces.put((x / stride + 0) * actualHeight + (y / stride + 0));
|
||||
faces.put((x / stride + 0) * actualHeight + (y / stride + 1));
|
||||
faces.put((x / stride + 1) * actualHeight + (y / stride + 0));
|
||||
faces.put((x / stride + 1) * actualHeight + (y / stride + 0));
|
||||
faces.put((x / stride + 0) * actualHeight + (y / stride + 1));
|
||||
faces.put((x / stride + 1) * actualHeight + (y / stride + 1));
|
||||
*/
|
||||
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 0);
|
||||
indexInserterPos++;
|
||||
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1);
|
||||
indexInserterPos++;
|
||||
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0);
|
||||
indexInserterPos++;
|
||||
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0);
|
||||
indexInserterPos++;
|
||||
indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1);
|
||||
indexInserterPos++;
|
||||
indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 1);
|
||||
indexInserterPos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices);
|
||||
|
||||
DBody body = collisionEngine.createDBody(triMesh);
|
||||
|
||||
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(body, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
||||
|
||||
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, body);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
public static DBody clientAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
|
||||
|
||||
Vector3d position = EntityUtils.getPosition(terrain);
|
||||
|
||||
DBody terrainBody = generateBodyFromTerrainData(Globals.clientSceneWrapper.getCollisionEngine(), data);
|
||||
|
||||
|
||||
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
||||
|
||||
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, terrainBody);
|
||||
|
||||
return terrainBody;
|
||||
}
|
||||
|
||||
|
||||
public static DBody serverAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
|
||||
|
||||
Vector3d position = EntityUtils.getPosition(terrain);
|
||||
Realm terrainRealm = Globals.realmManager.getEntityRealm(terrain);
|
||||
|
||||
DBody terrainBody = generateBodyFromTerrainData(terrainRealm.getCollisionEngine(),data);
|
||||
|
||||
|
||||
|
||||
terrainRealm.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN));
|
||||
|
||||
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, terrainBody);
|
||||
|
||||
return terrainBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an ode DBody from a terrain chunk data object
|
||||
* @param data The terrain data
|
||||
* @return The DBody
|
||||
*/
|
||||
private static DBody generateBodyFromTerrainData(CollisionEngine collisionEngine, TerrainChunkData data){
|
||||
DBody body = null;
|
||||
|
||||
//create data
|
||||
int numberTriangles = data.getFaceElements().size() / 3;
|
||||
int numberVertices = data.getVertices().size() / 3;
|
||||
|
||||
float[] vertices = new float[numberVertices * 3];
|
||||
int vertexInserterPos = 0;
|
||||
int[] indices = new int[numberTriangles * 3];
|
||||
int indexInserterPos = 0;
|
||||
|
||||
for(float vertexValue : data.getVertices()){
|
||||
vertices[vertexInserterPos] = vertexValue;
|
||||
vertexInserterPos++;
|
||||
}
|
||||
|
||||
for(int element : data.getFaceElements()){
|
||||
indices[indexInserterPos] = element;
|
||||
indexInserterPos++;
|
||||
}
|
||||
|
||||
//create trimesh
|
||||
if(vertices.length > 0){
|
||||
DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices);
|
||||
body = collisionEngine.createDBody(triMesh);
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a body from an AIScene
|
||||
* @param scene The AIScene to generate a rigid body off of
|
||||
* @return A rigid body based on the AIScene
|
||||
*/
|
||||
public static DBody generateRigidBodyFromAIScene(CollisionEngine collisionEngine, AIScene scene){
|
||||
|
||||
DBody body = collisionEngine.createDBody(null);
|
||||
|
||||
PointerBuffer meshesBuffer = scene.mMeshes();
|
||||
while(meshesBuffer.hasRemaining()){
|
||||
float[] verts;
|
||||
int numVertices;
|
||||
int[] indices;
|
||||
int numTriangles;
|
||||
|
||||
AIMesh aiMesh = AIMesh.create(meshesBuffer.get());
|
||||
//allocate array for vertices
|
||||
numVertices = aiMesh.mNumVertices();
|
||||
verts = new float[numVertices * 3];
|
||||
//read vertices
|
||||
AIVector3D.Buffer vertexBuffer = aiMesh.mVertices();
|
||||
int vertPos = 0;
|
||||
while(vertexBuffer.hasRemaining()){
|
||||
AIVector3D vector = vertexBuffer.get();
|
||||
verts[vertPos+0] = vector.x();
|
||||
verts[vertPos+1] = vector.y();
|
||||
verts[vertPos+2] = vector.z();
|
||||
vertPos = vertPos + 3;
|
||||
}
|
||||
numTriangles = aiMesh.mNumFaces();
|
||||
indices = new int[numTriangles * 3];
|
||||
int indicesPos = 0;
|
||||
//read faces
|
||||
AIFace.Buffer faceBuffer = aiMesh.mFaces();
|
||||
while(faceBuffer.hasRemaining()){
|
||||
AIFace currentFace = faceBuffer.get();
|
||||
IntBuffer indexBuffer = currentFace.mIndices();
|
||||
while(indexBuffer.hasRemaining()){
|
||||
int index = indexBuffer.get();
|
||||
indices[indicesPos] = index;
|
||||
indicesPos++;
|
||||
}
|
||||
}
|
||||
DTriMesh meshGeom = collisionEngine.createTrimeshGeom(verts, indices);
|
||||
meshGeom.setBody(body);
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current position of a rigid body as a joml vector
|
||||
* @param body The dbody
|
||||
* @return The position
|
||||
*/
|
||||
public static Vector3d getRigidBodyPosition(DBody body){
|
||||
return odeVecToJomlVec(body.getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rotation of an ode body as a joml quaternion
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public static Quaterniond getRigidBodyRotation(DBody body){
|
||||
return odeQuatToJomlQuat(body.getQuaternion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Ode vector to a joml vector
|
||||
* @param vector Ode vector
|
||||
* @return joml vector
|
||||
*/
|
||||
public static Vector3d odeVecToJomlVec(org.ode4j.math.DVector3C vector){
|
||||
return new Vector3d(vector.get0(),vector.get1(),vector.get2());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Ode quaternion to a joml quaternion
|
||||
* @param quaternion Ode quat
|
||||
* @return joml quat
|
||||
*/
|
||||
public static Quaterniond odeQuatToJomlQuat(DQuaternionC quaternion){
|
||||
return new Quaterniond(quaternion.get1(), quaternion.get2(), quaternion.get3(), quaternion.get0());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a joml quat to an ode quat
|
||||
* @param quaternion The joml quat
|
||||
* @return The ode quata
|
||||
*/
|
||||
public static DQuaternion jomlQuatToOdeQuat(Quaterniond quaternion){
|
||||
return new DQuaternion(quaternion.w, quaternion.x, quaternion.y, quaternion.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position + rotation of a body
|
||||
* @param position The position
|
||||
* @param rotation The rotation
|
||||
* @param body The body
|
||||
*/
|
||||
public static void setRigidBodyTransform(CollisionEngine collisionEngine, Vector3d position, Quaterniond rotation, DBody body){
|
||||
collisionEngine.setBodyTransform(body, position, rotation);
|
||||
}
|
||||
|
||||
//The width of a plane rigid body
|
||||
//It's really a box under the hood
|
||||
static final double PLANE_WIDTH = 0.3;
|
||||
|
||||
/**
|
||||
* Creates a plane DBody. Dimensions x and z control the length and width of the plane;
|
||||
* @param dimensions The dimensions of the plane
|
||||
* @return The DBody
|
||||
*/
|
||||
public static DBody createPlaneBody(CollisionEngine collisionEngine, Vector3d dimensions){
|
||||
DBox geom = collisionEngine.createCubeGeom(new Vector3d(dimensions.x,PLANE_WIDTH,dimensions.z));
|
||||
return collisionEngine.createDBody(geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cube DBody. Dimensions controlled by the dimensions vector.
|
||||
* @param collisionEngine The collision engine to create the body inside of
|
||||
* @param dimensions The dimensions of the cube
|
||||
* @return The DBody
|
||||
*/
|
||||
public static DBody createCubeBody(CollisionEngine collisionEngine, Vector3d dimensions){
|
||||
DBox geom = collisionEngine.createCubeGeom(new Vector3d(dimensions));
|
||||
return collisionEngine.createDBody(geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cylinder DBody. Dimensions controlled by the dimensions vector.
|
||||
* @param collisionEngine The collision engine to create the body inside of
|
||||
* @param dimensions The dimensions of the cube
|
||||
* @return The DBody
|
||||
*/
|
||||
public static DBody createCylinderBody(CollisionEngine collisionEngine, double radius, double length){
|
||||
DCylinder geom = collisionEngine.createCylinderGeom(radius,length);
|
||||
return collisionEngine.createDBody(geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a sphere body in the collision engine
|
||||
* @param collisionEngine The collision engine
|
||||
* @param radius The radius of the sphere
|
||||
* @return The DBody
|
||||
*/
|
||||
public static DBody createSphereBody(CollisionEngine collisionEngine, double radius){
|
||||
DSphere geom = collisionEngine.createSphereGeom(radius);
|
||||
return collisionEngine.createDBody(geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mass in the physics space from the body and mass value input
|
||||
* @param collisionEngine The collision engine to create the mass in
|
||||
* @param body The DBody to give mass to
|
||||
* @param mass The amount of mass to give to the DBody
|
||||
* @return THe DMass wrapping the DBody with a given mass value
|
||||
*/
|
||||
public static DMass createMassFromBody(CollisionEngine collisionEngine, DBody body, double mass){
|
||||
return collisionEngine.createDMass(mass, body);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
144
src/main/java/electrosphere/collision/RayCastCallback.java
Normal file
@ -0,0 +1,144 @@
|
||||
package electrosphere.collision;
|
||||
|
||||
import static org.ode4j.ode.OdeConstants.dContactBounce;
|
||||
import static org.ode4j.ode.OdeConstants.dContactSoftCFM;
|
||||
import static org.ode4j.ode.OdeConstants.dInfinity;
|
||||
import static org.ode4j.ode.OdeHelper.areConnectedExcluding;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DContact;
|
||||
import org.ode4j.ode.DContactBuffer;
|
||||
import org.ode4j.ode.DContactJoint;
|
||||
import org.ode4j.ode.DGeom;
|
||||
import org.ode4j.ode.DGeom.DNearCallback;
|
||||
import org.ode4j.ode.DRay;
|
||||
import org.ode4j.ode.OdeHelper;
|
||||
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.entity.Entity;
|
||||
|
||||
public class RayCastCallback implements DNearCallback {
|
||||
|
||||
static final int MAX_CONTACTS = 5;
|
||||
|
||||
/**
|
||||
* // Check ray collision against a space
|
||||
void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) {
|
||||
dReal *HitPosition = (dReal *)Data;
|
||||
|
||||
// Check collisions
|
||||
dContact Contacts[MAX_CONTACTS];
|
||||
int Count = dCollide(Geometry1, Geometry2, MAX_CONTACTS, &Contacts[0].geom, sizeof(dContact));
|
||||
for(int i = 0; i < Count; i++) {
|
||||
|
||||
// Check depth against current closest hit
|
||||
if(Contacts[i].geom.depth < HitPosition[3]) {
|
||||
dCopyVector3(HitPosition, Contacts[i].geom.pos);
|
||||
HitPosition[3] = Contacts[i].geom.depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//For the current execution, this stores the shortest length that has currently been encountered.
|
||||
//This is used to keep track of the closest body so that there doesn't need to be contact join creation.
|
||||
//This should be reset every time a ray cast is called in collision engine by calling setLength in this object.
|
||||
double shortestLength = 1000000;
|
||||
|
||||
@Override
|
||||
public void call(Object data, DGeom o1, DGeom o2) {
|
||||
// if (o1->body && o2->body) return;
|
||||
RayCastCallbackData rayCastData = (RayCastCallbackData)data;
|
||||
//null out potentially previous results
|
||||
// rayCastData.collisionPosition = null;
|
||||
// rayCastData.collidedEntity = null;
|
||||
// exit without doing anything if the two bodies are connected by a joint
|
||||
DBody b1 = o1.getBody();
|
||||
DBody b2 = o2.getBody();
|
||||
if (b1!=null && b2!=null && areConnectedExcluding (b1,b2,DContactJoint.class)) return;
|
||||
|
||||
//creates a buffer to store potential collisions
|
||||
DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box
|
||||
for (int i=0; i<MAX_CONTACTS; i++) {
|
||||
DContact contact = contacts.get(i);
|
||||
contact.surface.mode = dContactBounce | dContactSoftCFM;
|
||||
contact.surface.mu = dInfinity;
|
||||
contact.surface.mu2 = 0;
|
||||
contact.surface.bounce = 0.1;
|
||||
contact.surface.bounce_vel = 0.1;
|
||||
contact.surface.soft_cfm = 0.01;
|
||||
}
|
||||
Collidable collidable1 = rayCastData.bodyEntityMap.get(b1);
|
||||
Collidable collidable2 = rayCastData.bodyEntityMap.get(b2);
|
||||
if(
|
||||
(
|
||||
rayCastData.collidableTypeMask != null &&
|
||||
(
|
||||
(o1 instanceof DRay && rayCastData.collidableTypeMask.contains(collidable2.getType())) ||
|
||||
(o2 instanceof DRay && rayCastData.collidableTypeMask.contains(collidable1.getType()))
|
||||
)
|
||||
) ||
|
||||
rayCastData.collidableTypeMask == null
|
||||
){
|
||||
//calculate collisions
|
||||
int numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer());
|
||||
//create DContacts based on each collision that occurs
|
||||
if (numc != 0) {
|
||||
for (int i=0; i<numc; i++) {
|
||||
DContact contact = contacts.get(i);
|
||||
double depth = contact.geom.depth;
|
||||
|
||||
//check if should be stored in ray cast data return
|
||||
if(depth < shortestLength){
|
||||
shortestLength = depth;
|
||||
if(collidable1 != null){
|
||||
rayCastData.collidedEntity = collidable1.getParent();
|
||||
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
|
||||
} else if(collidable2 != null) {
|
||||
rayCastData.collidedEntity = collidable2.getParent();
|
||||
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
|
||||
} else if(rayCastData.collidableTypeMask == null){
|
||||
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length that the ray should travel
|
||||
* @param length The length
|
||||
*/
|
||||
protected void setLength(double length){
|
||||
this.shortestLength = length + 0.1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object that contains the information for a ray cast check
|
||||
*/
|
||||
static class RayCastCallbackData {
|
||||
//The map of ode DBody -> collidable
|
||||
Map<DBody,Collidable> bodyEntityMap;
|
||||
//The mask of collidable types to filter collisions by. Can be null.
|
||||
List<String> collidableTypeMask;
|
||||
//The entity that the ray cast collided with. If null, no entity was collided with.
|
||||
Entity collidedEntity = null;
|
||||
//The position in world space that the collision happened
|
||||
Vector3d collisionPosition = null;
|
||||
/**
|
||||
* Constructor
|
||||
* @param bodyEntityMap The map of ode DBody -> collidable
|
||||
* @param collidableTypeMask The mask of collidable types to filter collisions by. Can be null.
|
||||
*/
|
||||
public RayCastCallbackData(Map<DBody,Collidable> bodyEntityMap,List<String> collidableTypeMask){
|
||||
this.bodyEntityMap = bodyEntityMap;
|
||||
this.collidableTypeMask = collidableTypeMask;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
package electrosphere.game.collision.collidable;
|
||||
package electrosphere.collision.collidable;
|
||||
|
||||
import electrosphere.collision.shapes.CollisionShape;
|
||||
import electrosphere.dynamics.RigidBody;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.state.collidable.Impulse;
|
||||
import java.util.LinkedList;
|
||||
@ -73,6 +73,8 @@ import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import electrosphere.audio.AudioUtils;
|
||||
import electrosphere.client.targeting.crosshair.Crosshair;
|
||||
import electrosphere.client.terrain.editing.TerrainEditing;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.controls.Control.ControlMethod;
|
||||
import electrosphere.controls.Control.ControlType;
|
||||
import electrosphere.engine.Globals;
|
||||
@ -95,6 +97,7 @@ import electrosphere.entity.types.creature.CreatureUtils;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.menu.MenuGenerators;
|
||||
import electrosphere.menu.MenuGeneratorsDebug;
|
||||
import electrosphere.menu.MenuGeneratorsInGame;
|
||||
import electrosphere.menu.MenuGeneratorsInventory;
|
||||
import electrosphere.menu.WindowStrings;
|
||||
import electrosphere.menu.WindowUtils;
|
||||
@ -130,6 +133,8 @@ public class ControlHandler {
|
||||
public static final String INPUT_CODE_INVENTORY_OPEN = "inventoryOpen";
|
||||
public static final String INPUT_CODE_CHARACTER_OPEN = "characterOpen";
|
||||
public static final String INPUT_CODE_IRON_SIGHT = "ironSight";
|
||||
public static final String INPUT_CODE_PLACE_TERRAIN = "placeTerrain";
|
||||
public static final String INPUT_CODE_REMOVE_TERRAIN = "removeTerrain";
|
||||
|
||||
public static final String DATA_STRING_INPUT_CODE_MENU_NAVIGATE_FORWARD = "menuNavigateForward";
|
||||
public static final String DATA_STRING_INPUT_CODE_MENU_NAVIGATE_BACKWARDS = "menuNavigateBackwards";
|
||||
@ -320,6 +325,12 @@ public class ControlHandler {
|
||||
handler.addControl(INPUT_CODE_INVENTORY_ITEM_MANIPULATE, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_1));
|
||||
handler.addControl(INPUT_CODE_INVENTORY_ITEM_DRAG, new Control(ControlType.MOUSE_MOVEMENT,0));
|
||||
|
||||
/**
|
||||
Terrain controls
|
||||
*/
|
||||
handler.addControl(INPUT_CODE_PLACE_TERRAIN, new Control(ControlType.KEY,GLFW_KEY_T));
|
||||
handler.addControl(INPUT_CODE_REMOVE_TERRAIN, new Control(ControlType.KEY,GLFW_KEY_Y));
|
||||
|
||||
/*
|
||||
framestep controls
|
||||
*/
|
||||
@ -861,7 +872,7 @@ public class ControlHandler {
|
||||
// Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_MAIN_MENU);
|
||||
|
||||
// Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
|
||||
Window mainMenuInGame = MenuGenerators.createInGameMainMenu();
|
||||
Window mainMenuInGame = MenuGeneratorsInGame.createInGameMainMenu();
|
||||
// mainMenuWindow.addChild(mainMenuInGame);
|
||||
Globals.elementManager.registerWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, mainMenuInGame);
|
||||
WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), true);
|
||||
@ -922,6 +933,64 @@ public class ControlHandler {
|
||||
}});
|
||||
controls.get(INPUT_CODE_CHARACTER_OPEN).setRepeatTimeout(0.5f * Main.targetFrameRate);
|
||||
|
||||
mainGameControlList.add(controls.get(INPUT_CODE_PLACE_TERRAIN));
|
||||
controls.get(INPUT_CODE_PLACE_TERRAIN).setOnPress(new ControlMethod(){public void execute(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
Entity camera = Globals.playerCamera;
|
||||
if(
|
||||
collisionEngine != null &&
|
||||
camera != null
|
||||
){
|
||||
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
|
||||
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
|
||||
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
|
||||
TerrainEditing.editTerrain(cursorPos, 1.1f, 2, 0.01f);
|
||||
}
|
||||
}});
|
||||
controls.get(INPUT_CODE_PLACE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
Entity camera = Globals.playerCamera;
|
||||
if(
|
||||
collisionEngine != null &&
|
||||
camera != null
|
||||
){
|
||||
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
|
||||
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
|
||||
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
|
||||
TerrainEditing.editTerrain(cursorPos, 1.1f, 2, 0.01f);
|
||||
}
|
||||
}});
|
||||
controls.get(INPUT_CODE_PLACE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate);
|
||||
|
||||
mainGameControlList.add(controls.get(INPUT_CODE_REMOVE_TERRAIN));
|
||||
controls.get(INPUT_CODE_REMOVE_TERRAIN).setOnPress(new ControlMethod(){public void execute(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
Entity camera = Globals.playerCamera;
|
||||
if(
|
||||
collisionEngine != null &&
|
||||
camera != null
|
||||
){
|
||||
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
|
||||
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
|
||||
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
|
||||
TerrainEditing.editTerrain(cursorPos, 1.1f, 1, -0.01f);
|
||||
}
|
||||
}});
|
||||
controls.get(INPUT_CODE_REMOVE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
Entity camera = Globals.playerCamera;
|
||||
if(
|
||||
collisionEngine != null &&
|
||||
camera != null
|
||||
){
|
||||
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
|
||||
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
|
||||
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
|
||||
TerrainEditing.editTerrain(cursorPos, 1.1f, 1, -0.01f);
|
||||
}
|
||||
}});
|
||||
controls.get(INPUT_CODE_REMOVE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate);
|
||||
|
||||
}
|
||||
|
||||
void setInGameDebugControls(){
|
||||
@ -943,7 +1012,7 @@ public class ControlHandler {
|
||||
void setAlwaysOnDebugControls(){
|
||||
alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU));
|
||||
controls.get(DEBUG_OPEN_DEBUG_MENU).setOnPress(new ControlMethod(){public void execute(){
|
||||
System.out.println("open debug menu");
|
||||
LoggerInterface.loggerEngine.INFO("open debug menu");
|
||||
// Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
|
||||
Window mainMenuInGame = MenuGeneratorsDebug.createTopLevelDebugMenu();
|
||||
// mainMenuWindow.addChild(mainMenuInGame);
|
||||
|
||||
@ -12,6 +12,7 @@ import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import electrosphere.audio.AudioEngine;
|
||||
import electrosphere.auth.AuthenticationManager;
|
||||
import electrosphere.client.culling.ClientEntityCullingManager;
|
||||
import electrosphere.client.fluid.manager.ClientFluidManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
import electrosphere.client.player.ClientPlayerData;
|
||||
import electrosphere.client.scene.ClientSceneWrapper;
|
||||
@ -19,7 +20,8 @@ import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.sim.ClientSimulation;
|
||||
import electrosphere.client.terrain.cells.DrawCellManager;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.collision.dispatch.CollisionObject;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.collision.CollisionWorldData;
|
||||
import electrosphere.controls.CameraHandler;
|
||||
import electrosphere.controls.ControlCallback;
|
||||
import electrosphere.controls.ControlHandler;
|
||||
@ -30,8 +32,6 @@ import electrosphere.engine.loadingthreads.LoadingThread;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.Scene;
|
||||
import electrosphere.entity.types.hitbox.HitboxManager;
|
||||
import electrosphere.game.collision.CollisionEngine;
|
||||
import electrosphere.game.collision.CommonWorldData;
|
||||
import electrosphere.game.config.UserSettings;
|
||||
import electrosphere.game.server.structure.virtual.StructureManager;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
@ -52,7 +52,6 @@ import electrosphere.renderer.light.PointLight;
|
||||
import electrosphere.renderer.light.SpotLight;
|
||||
import electrosphere.renderer.loading.ModelPretransforms;
|
||||
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
|
||||
import electrosphere.renderer.meshgen.TreeModelGeneration;
|
||||
import electrosphere.renderer.shader.ShaderOptionMap;
|
||||
import electrosphere.renderer.texture.TextureMap;
|
||||
import electrosphere.renderer.ui.ElementManager;
|
||||
@ -62,9 +61,7 @@ import electrosphere.renderer.ui.font.RawFontMap;
|
||||
import electrosphere.script.ScriptEngine;
|
||||
import electrosphere.server.ai.AIManager;
|
||||
import electrosphere.server.datacell.EntityDataCellMapper;
|
||||
import electrosphere.server.datacell.GriddedDataCellManager;
|
||||
import electrosphere.server.datacell.RealmManager;
|
||||
import electrosphere.server.datacell.physics.DataCellPhysicsManager;
|
||||
import electrosphere.server.db.DatabaseController;
|
||||
import electrosphere.server.pathfinding.NavMeshManager;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
@ -157,7 +154,6 @@ public class Globals {
|
||||
public static ServerWorldData serverWorldData;
|
||||
public static RealmManager realmManager;
|
||||
public static EntityDataCellMapper entityDataCellMapper;
|
||||
public static DataCellPhysicsManager dataCellPhysicsManager;
|
||||
|
||||
//
|
||||
//Player manager
|
||||
@ -248,6 +244,9 @@ public class Globals {
|
||||
//macro simulation
|
||||
public static MacroSimulation macroSimulation;
|
||||
public static MacroData macroData;
|
||||
|
||||
//flag to only run a simulation without full client
|
||||
public static boolean RUN_SIMULATION_ONLY = false;
|
||||
|
||||
//micro simulation
|
||||
public static MicroSimulation microSimulation;
|
||||
@ -275,6 +274,9 @@ public class Globals {
|
||||
|
||||
//client terrain manager
|
||||
public static ClientTerrainManager clientTerrainManager;
|
||||
|
||||
//client fluid manager
|
||||
public static ClientFluidManager clientFluidManager;
|
||||
|
||||
//client player data
|
||||
public static ClientPlayerData clientPlayerData = new ClientPlayerData();
|
||||
@ -299,7 +301,7 @@ public class Globals {
|
||||
public static int openInventoriesCount = 0;
|
||||
|
||||
//collision world data
|
||||
public static CommonWorldData commonWorldData;
|
||||
public static CollisionWorldData commonWorldData;
|
||||
|
||||
//structure manager
|
||||
public static StructureManager structureManager;
|
||||
@ -380,12 +382,9 @@ public class Globals {
|
||||
clientHitboxManager = new HitboxManager();
|
||||
//ai manager
|
||||
aiManager = new AIManager();
|
||||
//data cell manager
|
||||
//realm & data cell manager
|
||||
realmManager = new RealmManager();
|
||||
// dataCellManager = new DataCellManager();
|
||||
// griddedDataCellManager = new GriddedDataCellManager();
|
||||
entityDataCellMapper = new EntityDataCellMapper();
|
||||
// dataCellLocationResolver = new DataCellLocationResolver();
|
||||
//nav mesh manager
|
||||
navMeshManager = new NavMeshManager();
|
||||
//terrain
|
||||
@ -442,12 +441,13 @@ public class Globals {
|
||||
//init default shaderProgram
|
||||
defaultMeshShader = ShaderProgram.smart_assemble_shader(false,true);
|
||||
//init terrain shader program
|
||||
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain/terrain.vs", "/Shaders/terrain/terrain.fs");
|
||||
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");
|
||||
//init skybox
|
||||
assetManager.registerModelToSpecificString(RenderUtils.createSkyboxModel(null), AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
|
||||
//init leaves
|
||||
assetManager.registerModelToSpecificString(TreeModelGeneration.generateLeavesModel(), AssetDataStrings.LEAVES_MODEL);
|
||||
//init models
|
||||
assetManager.addModelPathToQueue("Models/unitsphere.fbx");
|
||||
assetManager.addModelPathToQueue("Models/unitsphere_1.fbx");
|
||||
|
||||
@ -6,19 +6,18 @@ import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.ode4j.ode.OdeHelper;
|
||||
|
||||
import electrosphere.audio.AudioEngine;
|
||||
import electrosphere.client.sim.ClientFunctions;
|
||||
import electrosphere.controls.ControlHandler;
|
||||
import electrosphere.engine.cli.CLIParser;
|
||||
import electrosphere.engine.loadingthreads.LoadingThread;
|
||||
import electrosphere.game.config.UserSettings;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.parser.net.raw.NetworkParser;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.util.worldviewer.TerrainViewer;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
|
||||
|
||||
|
||||
@ -80,6 +79,9 @@ public class Main {
|
||||
//init global variables
|
||||
Globals.initGlobals();
|
||||
|
||||
//init ODE
|
||||
OdeHelper.initODE();
|
||||
|
||||
// if(1==1){
|
||||
// SaveUtils.loadSave("arena");
|
||||
// Globals.authenticationManager = new AuthenticationManager();
|
||||
@ -92,22 +94,21 @@ public class Main {
|
||||
|
||||
//world gen testing
|
||||
//gen terrain
|
||||
// Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,0.0f,0);
|
||||
// Globals.serverTerrainManager.load();
|
||||
// Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager);
|
||||
// //gen world
|
||||
// Globals.macroData = MacroData.generateWorld(0);
|
||||
// Globals.macroData.describeWorld();
|
||||
// boolean run = true;
|
||||
// Globals.macroSimulation = new MacroSimulation();
|
||||
// while(run){
|
||||
// Globals.macroSimulation.simulate();
|
||||
// try {
|
||||
// TimeUnit.MILLISECONDS.sleep(1000);
|
||||
// } catch (InterruptedException ex) {
|
||||
// ex.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
if(Globals.RUN_SIMULATION_ONLY){
|
||||
//gen world
|
||||
Globals.macroData = MacroData.generateWorld(0);
|
||||
Globals.macroData.describeWorld();
|
||||
boolean run = true;
|
||||
Globals.macroSimulation = new MacroSimulation();
|
||||
while(run){
|
||||
Globals.macroSimulation.simulate();
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if(1==1){
|
||||
// Globals.audioEngine = new AudioEngine();
|
||||
@ -233,7 +234,7 @@ public class Main {
|
||||
/// C L I E N T N E T W O R K I N G S T U F F
|
||||
///
|
||||
//Why is this its own function? Just to get the networking code out of main()
|
||||
if(Globals.RUN_CLIENT && Globals.clientConnection != null){
|
||||
if(Globals.clientConnection != null){
|
||||
Globals.clientConnection.parseMessages();
|
||||
}
|
||||
|
||||
@ -318,7 +319,9 @@ public class Main {
|
||||
running = false;
|
||||
if(Globals.server != null){
|
||||
Globals.server.close();
|
||||
Globals.serverThread.interrupt();
|
||||
if(Globals.serverThread != null){
|
||||
Globals.serverThread.interrupt();
|
||||
}
|
||||
}
|
||||
//shut down audio engine
|
||||
if(!Globals.HEADLESS && Globals.RUN_CLIENT){
|
||||
@ -328,6 +331,8 @@ public class Main {
|
||||
if(Globals.netMonitor != null){
|
||||
Globals.netMonitor.close();
|
||||
}
|
||||
//shutdown ode
|
||||
OdeHelper.closeODE();
|
||||
}
|
||||
|
||||
public static long getCurrentFrame(){
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package electrosphere.engine.assetmanager;
|
||||
|
||||
import electrosphere.audio.AudioBuffer;
|
||||
import electrosphere.collision.dispatch.CollisionObject;
|
||||
import electrosphere.game.collision.PhysicsUtils;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.collision.PhysicsUtils;
|
||||
import electrosphere.renderer.Mesh;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.ShaderProgram;
|
||||
@ -21,6 +21,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.lwjgl.assimp.AIScene;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DWorld;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -41,8 +43,8 @@ public class AssetManager {
|
||||
Map<String,ShaderProgram> shadersLoadedIntoMemory = new ConcurrentHashMap<String,ShaderProgram>();
|
||||
List<ActorShaderMask> shadersInQueue = new CopyOnWriteArrayList<ActorShaderMask>();
|
||||
|
||||
Map<String,CollisionObject> physicsMeshesLoadedIntoMemory = new ConcurrentHashMap<String,CollisionObject>();
|
||||
List<String> physicsMeshesToLoad = new CopyOnWriteArrayList<String>();
|
||||
Map<String,DBody> physicsMeshesLoadedIntoMemory = new ConcurrentHashMap<String,DBody>();
|
||||
List<PhysicsMeshQueueItem> physicsMeshesToLoad = new CopyOnWriteArrayList<PhysicsMeshQueueItem>();
|
||||
|
||||
Map<String,PoseModel> poseModelsLoadedIntoMemory = new ConcurrentHashMap<String,PoseModel>();
|
||||
List<String> poseModelsInQueue = new CopyOnWriteArrayList<String>();
|
||||
@ -67,12 +69,17 @@ public class AssetManager {
|
||||
//models
|
||||
for(String currentPath : modelsInQueue){
|
||||
modelsInQueue.remove(currentPath);
|
||||
AIScene scene = ModelLoader.loadAIScene(currentPath);
|
||||
modelsLoadedIntoMemory.put(currentPath, ModelLoader.createModelFromAiScene(scene,currentPath));
|
||||
if(physicsMeshesToLoad.contains(currentPath)){
|
||||
//create physics
|
||||
physicsMeshesToLoad.remove(currentPath);
|
||||
physicsMeshesLoadedIntoMemory.put(currentPath,PhysicsUtils.generateRigidBodyFromAIScene(scene));
|
||||
AIScene aiScene = ModelLoader.loadAIScene(currentPath);
|
||||
modelsLoadedIntoMemory.put(currentPath, ModelLoader.createModelFromAiScene(aiScene,currentPath));
|
||||
for(PhysicsMeshQueueItem physicsMeshQueueItem : physicsMeshesToLoad){
|
||||
if(physicsMeshQueueItem.modelPath.contains(currentPath)){
|
||||
//create physics
|
||||
physicsMeshesToLoad.remove(physicsMeshQueueItem);
|
||||
physicsMeshesLoadedIntoMemory.put(
|
||||
getCollisionMeshMapKey(physicsMeshQueueItem.collisionEngine,currentPath),
|
||||
PhysicsUtils.generateRigidBodyFromAIScene(physicsMeshQueueItem.collisionEngine,aiScene)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//textures from disk to gpu
|
||||
@ -345,14 +352,29 @@ public class AssetManager {
|
||||
//
|
||||
//COLLISION MESH
|
||||
//
|
||||
public void addCollisionMeshToQueue(String path){
|
||||
if(!physicsMeshesToLoad.contains(path) && !physicsMeshesLoadedIntoMemory.containsKey(path)){
|
||||
physicsMeshesToLoad.add(path);
|
||||
public void addCollisionMeshToQueue(PhysicsMeshQueueItem physicsMeshQueueItem){
|
||||
if(
|
||||
!physicsMeshesToLoad.contains(physicsMeshQueueItem) &&
|
||||
!physicsMeshesLoadedIntoMemory.containsKey(getCollisionMeshMapKey(
|
||||
physicsMeshQueueItem.collisionEngine,
|
||||
physicsMeshQueueItem.modelPath
|
||||
))){
|
||||
physicsMeshesToLoad.add(physicsMeshQueueItem);
|
||||
}
|
||||
}
|
||||
|
||||
public CollisionObject fetchCollisionObject(String path){
|
||||
return physicsMeshesLoadedIntoMemory.get(path);
|
||||
public DBody fetchCollisionObject(CollisionEngine collisionEngine, String path){
|
||||
return physicsMeshesLoadedIntoMemory.get(getCollisionMeshMapKey(collisionEngine,path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key based on collision engine object hash and path of model
|
||||
* @param collisionEngine collision engine
|
||||
* @param path The path
|
||||
* @return The key
|
||||
*/
|
||||
private String getCollisionMeshMapKey(CollisionEngine collisionEngine, String path){
|
||||
return collisionEngine + path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package electrosphere.engine.assetmanager;
|
||||
|
||||
import org.ode4j.ode.DSpace;
|
||||
import org.ode4j.ode.DWorld;
|
||||
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
|
||||
/**
|
||||
* Item in a queue of physics meshes to be loaded from disk. Relates world to model path.
|
||||
*/
|
||||
public class PhysicsMeshQueueItem {
|
||||
|
||||
CollisionEngine collisionEngine;
|
||||
String modelPath;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param collisionEngine The collision engine
|
||||
* @param modelPath The path to the model
|
||||
*/
|
||||
public PhysicsMeshQueueItem(CollisionEngine collisionEngine, String modelPath){
|
||||
this.collisionEngine = collisionEngine;
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,6 +17,9 @@ public class CLIParser {
|
||||
Globals.RUN_SERVER = true;
|
||||
Globals.HEADLESS = true;
|
||||
} break;
|
||||
case "--simulate": {
|
||||
Globals.RUN_SIMULATION_ONLY = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import org.joml.Vector3d;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.ServerEntityUtils;
|
||||
@ -36,8 +37,6 @@ public class ArenaLoading {
|
||||
LoadingUtils.initAuthenticationManager();
|
||||
//initialize the server thread (server only)
|
||||
LoadingUtils.initServerThread();
|
||||
//collision engine
|
||||
LoadingUtils.initCommonWorldData(Globals.RUN_SERVER);
|
||||
//init gridded datacell manager
|
||||
LoadingUtils.initGriddedRealm();
|
||||
//initialize the "virtual" objects simulation
|
||||
@ -152,6 +151,11 @@ public class ArenaLoading {
|
||||
// Entity sword = ItemUtils.spawnBasicItem("Katana");
|
||||
// EntityUtils.initiallyPositionEntity(sword, new Vector3d(1,0.4f,2));
|
||||
// EntityUtils.getRotation(sword).set(new Quaternionf().rotationY((float)(Math.PI/2.0)));
|
||||
|
||||
|
||||
// Entity leafBlock = EntityCreationUtils.createClientSpatialEntity();
|
||||
// EntityCreationUtils.makeEntityDrawable(leafBlock, "Models/foliageBlockTemplate1Test1.fbx");
|
||||
// EntityUtils.getPosition(leafBlock).set(3,3,3);
|
||||
|
||||
//floating island 1
|
||||
// Entity island1 = ObjectUtils.spawnBasicObject("floatingisland1");
|
||||
|
||||
@ -1,16 +1,30 @@
|
||||
package electrosphere.engine.loadingthreads;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import electrosphere.client.culling.ClientEntityCullingManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
import electrosphere.client.sim.ClientSimulation;
|
||||
import electrosphere.client.targeting.crosshair.Crosshair;
|
||||
import electrosphere.client.terrain.cells.DrawCellManager;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.controls.ControlHandler;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.DrawableUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.state.BehaviorTree;
|
||||
import electrosphere.entity.state.movement.ApplyRotationTree;
|
||||
import electrosphere.entity.types.camera.CameraEntityUtils;
|
||||
import electrosphere.entity.types.tree.ProceduralTree;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.menu.MenuGenerators;
|
||||
import electrosphere.menu.MenuGeneratorsMultiplayer;
|
||||
@ -19,12 +33,7 @@ import electrosphere.menu.WindowUtils;
|
||||
import electrosphere.net.NetUtils;
|
||||
import electrosphere.net.client.ClientNetworking;
|
||||
import electrosphere.renderer.ui.Window;
|
||||
import electrosphere.client.culling.ClientEntityCullingManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
import electrosphere.client.sim.ClientSimulation;
|
||||
import electrosphere.client.targeting.crosshair.Crosshair;
|
||||
import electrosphere.client.terrain.cells.DrawCellManager;
|
||||
import electrosphere.controls.ControlHandler;
|
||||
import electrosphere.server.datacell.EntityDataCellMapper;
|
||||
|
||||
public class ClientLoading {
|
||||
|
||||
@ -75,12 +84,10 @@ public class ClientLoading {
|
||||
loadingWindow.setVisible(true);
|
||||
//disable menu input
|
||||
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT);
|
||||
//collision engine
|
||||
if(!Globals.RUN_SERVER){
|
||||
LoadingUtils.initCommonWorldData(Globals.RUN_SERVER);
|
||||
}
|
||||
//initialize the "real" objects simulation
|
||||
initClientSimulation();
|
||||
//init foliage manager
|
||||
initFoliageManager();
|
||||
//initialize the cell manager (client)
|
||||
initDrawCellManager();
|
||||
//initialize the basic graphical entities of the world (skybox, camera)
|
||||
@ -91,8 +98,6 @@ public class ClientLoading {
|
||||
setSimulationsToReady();
|
||||
//init culling manager and other graphics-focused non-simulation items
|
||||
initEntityCullingManager();
|
||||
//init foliage manager
|
||||
initFoliageManager();
|
||||
//hide cursor
|
||||
Globals.controlHandler.hideMouse();
|
||||
//make loading window disappear
|
||||
@ -120,6 +125,9 @@ public class ClientLoading {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Inits the client networking thread and socket
|
||||
*/
|
||||
private static void initClientThread(){
|
||||
//start client networking
|
||||
Thread clientThread = null;
|
||||
@ -206,24 +214,75 @@ public class ClientLoading {
|
||||
EntityCreationUtils.makeEntityDrawable(cloudRing, "Models/cloudRing.fbx");
|
||||
EntityUtils.getRotation(cloudRing).rotateX((float)(-Math.PI/2.0f));
|
||||
EntityUtils.getScale(cloudRing).mul(1000.0f);
|
||||
Globals.clientScene.registerBehaviorTree(new ApplyRotationTree(cloudRing,new Quaternionf().rotationZ(0.0001f)));
|
||||
Globals.clientScene.registerBehaviorTree(new ApplyRotationTree(cloudRing,new Quaterniond().rotationZ(0.0001)));
|
||||
Globals.assetManager.queueOverrideMeshShader("Models/cloudRing.fbx", "Sphere", "Shaders/skysphere/skysphere.vs", "Shaders/skysphere/skysphere.fs");
|
||||
|
||||
Entity cursorTracker = EntityCreationUtils.createClientSpatialEntity();
|
||||
EntityCreationUtils.makeEntityDrawable(cursorTracker, "Models/unitsphere_1.fbx");
|
||||
EntityUtils.getScale(cursorTracker).set(0.3f);
|
||||
Globals.clientSceneWrapper.getScene().registerBehaviorTree(new BehaviorTree() {
|
||||
@Override
|
||||
public void simulate(float deltaTime) {
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
Entity camera = Globals.playerCamera;
|
||||
if(
|
||||
collisionEngine != null &&
|
||||
camera != null
|
||||
){
|
||||
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
|
||||
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
|
||||
Vector3d cursorPos = collisionEngine.rayCastPosition(centerPos, new Vector3d(eyePos).mul(-1.0), 5.0);
|
||||
if(cursorPos != null){
|
||||
EntityUtils.getPosition(cursorTracker).set(cursorPos);
|
||||
} else {
|
||||
EntityUtils.getPosition(cursorTracker).set(new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-5.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Random rand = new Random(0);
|
||||
{
|
||||
Entity tree = ProceduralTree.clientGenerateProceduralTree("oak", rand.nextLong());
|
||||
EntityUtils.getPosition(tree).set(5,0,5);
|
||||
}
|
||||
// for(int i = 0; i < 6; i++){
|
||||
// Entity tree = ProceduralTree.clientGenerateProceduralTree("oak", rand.nextLong());
|
||||
// // EntityUtils.getPosition(tree).set(5,0,5);
|
||||
// // EntityUtils.getScale(tree).set(0.5f);
|
||||
// // EntityUtils.getRotation(tree).rotateLocalX(0.5);
|
||||
// EntityUtils.getPosition(tree).set(5,0,i * 5);
|
||||
// }
|
||||
|
||||
for(int x = 0; x < 5; x++){
|
||||
for(int z = 0; z < 5; z++){
|
||||
Entity tree = ProceduralTree.clientGenerateProceduralTree("oak", rand.nextLong());
|
||||
ClientEntityUtils.initiallyPositionEntity(tree, new Vector3d(5 + x * 5,0,5 + z * 5));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void initDrawCellManager(){
|
||||
while(Globals.clientWorldData == null){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
//initialize draw cell manager
|
||||
Globals.drawCellManager = new DrawCellManager(Globals.commonWorldData, Globals.clientTerrainManager, 0, 0);
|
||||
Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
|
||||
//set our draw cell manager to actually generate drawable chunks
|
||||
Globals.drawCellManager.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.drawCellManager.containsInvalidCell()){
|
||||
while(Globals.drawCellManager.containsUnrequestedCell()){
|
||||
// Globals.drawCellManager.updateInvalidCell();
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// System.out.println("invalid cell");
|
||||
}
|
||||
@ -233,16 +292,17 @@ public class ClientLoading {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// System.out.println("undrawable");
|
||||
}
|
||||
|
||||
while(Globals.drawCellManager.containsPhysicsNeedingCell()){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
// while(Globals.drawCellManager.containsPhysicsNeedingCell()){
|
||||
// try {
|
||||
// TimeUnit.MILLISECONDS.sleep(10);
|
||||
// } catch (InterruptedException ex) {
|
||||
// }
|
||||
// }
|
||||
// System.out.println("Draw Cell Manager ready");
|
||||
}
|
||||
|
||||
|
||||
@ -13,8 +13,11 @@ import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.menu.MenuGenerators;
|
||||
import electrosphere.menu.WindowStrings;
|
||||
import electrosphere.menu.WindowUtils;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.renderer.ui.Window;
|
||||
import electrosphere.server.saves.SaveUtils;
|
||||
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.util.FileUtils;
|
||||
import electrosphere.controls.ControlHandler;
|
||||
@ -29,7 +32,8 @@ public class DebugSPWorldLoading {
|
||||
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
|
||||
loadingWindow.setVisible(true);
|
||||
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,0.0f,0);
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator());
|
||||
Globals.currentSaveName = "random_sp_world";
|
||||
if(!SaveUtils.getSaves().contains("random_sp_world")){
|
||||
//
|
||||
//the juicy server GENERATION part
|
||||
@ -38,7 +42,7 @@ public class DebugSPWorldLoading {
|
||||
SaveUtils.createOrOverwriteSave("random_sp_world");
|
||||
//create terrain
|
||||
Globals.serverTerrainManager.generate();
|
||||
Globals.serverTerrainManager.save(SaveUtils.deriveSaveDirectoryPath("random_sp_world"));
|
||||
Globals.serverTerrainManager.save("random_sp_world");
|
||||
//create world.json
|
||||
Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager);
|
||||
FileUtils.serializeObjectToSavePath("random_sp_world", "./world.json", Globals.serverWorldData);
|
||||
@ -53,18 +57,22 @@ public class DebugSPWorldLoading {
|
||||
|
||||
|
||||
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
|
||||
//init the data of the world
|
||||
// LoadingUtils.initTerrainDataCellManager();
|
||||
//init authentication
|
||||
LoadingUtils.initAuthenticationManager();
|
||||
//initialize the local connection
|
||||
Globals.clientUsername = "testuser";
|
||||
Globals.clientPassword = AuthenticationManager.getHashedString("testpass");
|
||||
LoadingUtils.initLocalConnection();
|
||||
ServerConnectionHandler serverPlayerConnection = LoadingUtils.initLocalConnection();
|
||||
//wait for player object creation
|
||||
while(Globals.playerManager.getPlayers().size() < 1){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//initialize the "real" objects simulation
|
||||
LoadingUtils.initMicroSimulation();
|
||||
//collision engine
|
||||
LoadingUtils.initCommonWorldData(Globals.RUN_SERVER);
|
||||
//init game specific stuff (ie different skybox colors)
|
||||
LoadingUtils.initGameGraphicalEntities();
|
||||
//set simulations to ready if they exist
|
||||
@ -79,57 +87,14 @@ public class DebugSPWorldLoading {
|
||||
} catch (InterruptedException ex) {}
|
||||
}
|
||||
|
||||
|
||||
LoadingUtils.spawnLocalPlayerTestEntity();
|
||||
//spawn player character
|
||||
LoadingUtils.spawnLocalPlayerTestEntity(serverPlayerConnection);
|
||||
|
||||
initWorldBaseGraphicalEntities();
|
||||
//request terrain data
|
||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
|
||||
|
||||
// SeekTown.attachToCreature(Globals.playerEntity);
|
||||
|
||||
//hide cursor
|
||||
Globals.controlHandler.hideMouse();
|
||||
//make loading window disappear
|
||||
loadingWindow.setVisible(false);
|
||||
//recapture screen
|
||||
Globals.controlHandler.setShouldRecapture(true);
|
||||
//set rendering flags to main game mode
|
||||
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
|
||||
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = true;
|
||||
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = true;
|
||||
Globals.RENDER_FLAG_RENDER_UI = true;
|
||||
Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false;
|
||||
Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false;
|
||||
LoggerInterface.loggerEngine.INFO("[Client]Finished loading main game");
|
||||
//set controls state
|
||||
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME);
|
||||
}
|
||||
|
||||
|
||||
private static void initWorldBaseGraphicalEntities(){
|
||||
/*
|
||||
|
||||
Skybox
|
||||
|
||||
*/
|
||||
// Model skyboxModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
|
||||
// Globals.skybox = EntityUtils.spawnDrawableEntity(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Player Camera
|
||||
|
||||
*/
|
||||
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));
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Targeting crosshair
|
||||
*/
|
||||
Crosshair.initCrossHairEntity();
|
||||
|
||||
//Run client startup process
|
||||
ClientLoading.loadClientWorld();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package electrosphere.engine.loadingthreads;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -13,6 +17,7 @@ import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.auth.AuthenticationManager;
|
||||
import electrosphere.client.terrain.cells.DrawCellManager;
|
||||
import electrosphere.collision.CollisionWorldData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
@ -20,7 +25,6 @@ import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.entity.state.movement.ApplyRotationTree;
|
||||
import electrosphere.entity.types.creature.CreatureTemplate;
|
||||
import electrosphere.entity.types.creature.CreatureUtils;
|
||||
import electrosphere.game.collision.CommonWorldData;
|
||||
import electrosphere.game.data.creature.type.CreatureType;
|
||||
import electrosphere.game.data.creature.type.visualattribute.VisualAttribute;
|
||||
import electrosphere.game.server.town.Town;
|
||||
@ -28,13 +32,16 @@ import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.net.NetUtils;
|
||||
import electrosphere.net.client.ClientNetworking;
|
||||
import electrosphere.net.parser.net.message.CharacterMessage;
|
||||
import electrosphere.net.server.Server;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.server.datacell.GriddedDataCellManager;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.saves.SaveUtils;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
import electrosphere.server.simulation.MicroSimulation;
|
||||
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
/**
|
||||
@ -54,7 +61,7 @@ public class LoadingUtils {
|
||||
Actually initialize the terrain manager
|
||||
*/
|
||||
float randomDampener = 0.0f; //0.25f;
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,randomDampener,0);
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,randomDampener,0,new OverworldChunkGenerator());
|
||||
if(Globals.RUN_SERVER){
|
||||
if(Globals.userSettings.gameplayGenerateWorld()){
|
||||
Globals.serverTerrainManager.generate();
|
||||
@ -70,7 +77,6 @@ public class LoadingUtils {
|
||||
int playerStartX = 0;
|
||||
int playerStartY = 0;
|
||||
int discreteSize = Globals.serverTerrainManager.getWorldDiscreteSize();
|
||||
int chunkSize = Globals.serverTerrainManager.getChunkWidth();
|
||||
boolean found = false;
|
||||
for(int x = 0; x < discreteSize; x++){
|
||||
for(int y = 0; y < discreteSize; y++){
|
||||
@ -108,32 +114,32 @@ public class LoadingUtils {
|
||||
Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager);
|
||||
}
|
||||
|
||||
static void initCommonWorldData(boolean FLAG_INIT_SERVER){
|
||||
if(Globals.commonWorldData == null){
|
||||
if(FLAG_INIT_SERVER){
|
||||
Globals.commonWorldData = new CommonWorldData(Globals.serverWorldData, Globals.serverTerrainManager);
|
||||
if(Globals.macroSimulation != null){
|
||||
Town startTown = Globals.macroData.getTowns().get(0);
|
||||
Vector2i firstPos = startTown.getPositions().get(0);
|
||||
// double startX = firstPos.x * Globals.serverTerrainManager.getChunkWidth();
|
||||
// double startZ = firstPos.y * Globals.serverTerrainManager.getChunkWidth();
|
||||
double startX = Globals.commonWorldData.convertWorldToReal(firstPos.x);
|
||||
double startZ = Globals.commonWorldData.convertWorldToReal(firstPos.y);
|
||||
Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ);
|
||||
}
|
||||
} else {
|
||||
//basically wait for the client to receive the world metadata
|
||||
while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(5);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
//then create common world data
|
||||
Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
// static void initCommonWorldData(boolean FLAG_INIT_SERVER){
|
||||
// if(Globals.commonWorldData == null){
|
||||
// if(FLAG_INIT_SERVER){
|
||||
// Globals.commonWorldData = new CommonWorldData(Globals.serverWorldData, Globals.serverTerrainManager);
|
||||
// if(Globals.macroSimulation != null){
|
||||
// Town startTown = Globals.macroData.getTowns().get(0);
|
||||
// Vector2i firstPos = startTown.getPositions().get(0);
|
||||
// // double startX = firstPos.x * Globals.serverTerrainManager.getChunkWidth();
|
||||
// // double startZ = firstPos.y * Globals.serverTerrainManager.getChunkWidth();
|
||||
// double startX = Globals.commonWorldData.convertWorldToReal(firstPos.x);
|
||||
// double startZ = Globals.commonWorldData.convertWorldToReal(firstPos.y);
|
||||
// Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ);
|
||||
// }
|
||||
// } else {
|
||||
// //basically wait for the client to receive the world metadata
|
||||
// while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){
|
||||
// try {
|
||||
// TimeUnit.MILLISECONDS.sleep(5);
|
||||
// } catch (InterruptedException ex) {
|
||||
// }
|
||||
// }
|
||||
// //then create common world data
|
||||
// Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@ -164,25 +170,30 @@ public class LoadingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void initLocalConnection(){
|
||||
Globals.server = new Server(NetUtils.getPort());
|
||||
static final int STREAM_BUFFER_SIZE = 32 * 1024 * 1024;
|
||||
static ServerConnectionHandler initLocalConnection(){
|
||||
ServerConnectionHandler rVal = null;
|
||||
try {
|
||||
Globals.server = new Server(NetUtils.getPort());
|
||||
//client -> server pipe
|
||||
PipedInputStream clientInput = new PipedInputStream();
|
||||
PipedInputStream clientInput = new PipedInputStream(STREAM_BUFFER_SIZE);
|
||||
PipedOutputStream serverOutput = new PipedOutputStream(clientInput);
|
||||
//server -> client pipe
|
||||
PipedInputStream serverInput = new PipedInputStream();
|
||||
PipedOutputStream clientOutput = new PipedOutputStream(serverInput);
|
||||
PipedOutputStream clientOutput;
|
||||
clientOutput = new PipedOutputStream(serverInput);
|
||||
//start server communication thread
|
||||
Globals.server.addLocalPlayer(serverInput, serverOutput);
|
||||
rVal = Globals.server.addLocalPlayer(serverInput, serverOutput);
|
||||
//start client communication thread
|
||||
Globals.clientConnection = new ClientNetworking(clientInput,clientOutput);
|
||||
Thread clientThread = null;
|
||||
clientThread = new Thread(Globals.clientConnection);
|
||||
clientThread.start();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,7 +230,7 @@ public class LoadingUtils {
|
||||
/**
|
||||
* Spawns the character, and sets server side connection player object values to the appropriate chunk
|
||||
*/
|
||||
static void spawnLocalPlayerTestEntity(){
|
||||
static void spawnLocalPlayerTestEntity(ServerConnectionHandler serverPlayerConnection){
|
||||
//
|
||||
//Create entity
|
||||
//
|
||||
@ -238,10 +249,9 @@ public class LoadingUtils {
|
||||
template.putValue(attribute.getAttributeId(), attribute.getVariants().get(0).getId());
|
||||
}
|
||||
}
|
||||
Realm realm = Globals.realmManager.getRealms().iterator().next();
|
||||
//spawn entity
|
||||
Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,Globals.spawnPoint,template.getCreatureType(),template);
|
||||
Globals.playerEntity = newPlayerEntity;
|
||||
//set player character template
|
||||
serverPlayerConnection.setCreatureTemplate(template);
|
||||
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());
|
||||
|
||||
//set player world-space coordinates
|
||||
Player playerObject = Globals.playerManager.getPlayerFromId(0);
|
||||
@ -250,12 +260,6 @@ public class LoadingUtils {
|
||||
Globals.serverWorldData.convertRealToChunkSpace(Globals.spawnPoint.y),
|
||||
Globals.serverWorldData.convertRealToChunkSpace(Globals.spawnPoint.z)
|
||||
));
|
||||
//set client entity data
|
||||
Globals.clientPlayerData.setWorldPos(playerObject.getWorldPos());
|
||||
//initially position entity
|
||||
ServerEntityUtils.initiallyPositionEntity(realm, newPlayerEntity, new Vector3d(Globals.spawnPoint.x + 1,Globals.spawnPoint.y + 5,Globals.spawnPoint.z + 1));
|
||||
//add entity to correct cells
|
||||
realm.getDataCellManager().addPlayerToRealm(playerObject);
|
||||
}
|
||||
|
||||
static void initMacroSimulation(){
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.engine.loadingthreads;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.server.saves.SaveUtils;
|
||||
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
public class ServerLoading {
|
||||
@ -15,7 +16,7 @@ public class ServerLoading {
|
||||
// }
|
||||
|
||||
//TODO: Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,randomDampener,0);
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,0.0f,0);
|
||||
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator());
|
||||
SaveUtils.loadSave(Globals.currentSaveName);
|
||||
// LoadingUtils.initTerrainDataCellManager();
|
||||
//TODO: set spawnpoint
|
||||
@ -32,8 +33,6 @@ public class ServerLoading {
|
||||
LoadingUtils.initMacroSimulation();
|
||||
//initialize the "real" objects simulation
|
||||
LoadingUtils.initMicroSimulation();
|
||||
//collision engine
|
||||
LoadingUtils.initCommonWorldData(Globals.RUN_SERVER);
|
||||
//init game specific stuff (ie different skybox colors)
|
||||
LoadingUtils.initGameGraphicalEntities();
|
||||
//set simulations to ready if they exist
|
||||
|
||||
@ -18,7 +18,7 @@ public class ClientEntityUtils {
|
||||
*/
|
||||
public static void initiallyPositionEntity(Entity entity, Vector3d position){
|
||||
//reposition entity
|
||||
CollisionObjUtils.positionCharacter(entity, position);
|
||||
CollisionObjUtils.clientPositionCharacter(entity, position);
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
src/main/java/electrosphere/entity/DrawableUtils.java
Normal file
@ -0,0 +1,17 @@
|
||||
package electrosphere.entity;
|
||||
|
||||
/**
|
||||
* Utilities to manipulating drawable entities (eg making an entity transparent)
|
||||
*/
|
||||
public class DrawableUtils {
|
||||
|
||||
/**
|
||||
* Edits entity data to make the entity transparent
|
||||
* @param entity The entity to edit
|
||||
*/
|
||||
public static void makeEntityTransparent(Entity entity){
|
||||
entity.putData(EntityDataStrings.DRAW_TRANSPARENT_PASS, true);
|
||||
entity.removeData(EntityDataStrings.DRAW_SOLID_PASS);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.entity;
|
||||
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
@ -80,7 +81,7 @@ public class EntityCreationUtils {
|
||||
public static void makeEntityPoseable(Entity entity, String modelPath){
|
||||
entity.putData(EntityDataStrings.POSE_ACTOR, new PoseActor(modelPath));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
@ -95,7 +96,23 @@ public class EntityCreationUtils {
|
||||
public static void makeEntityDrawable(Entity entity, String modelPath){
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
Globals.clientScene.registerEntity(entity);
|
||||
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAWABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* MAkes an already created entity a drawable entity (client only) by backing it with an Actor
|
||||
* @param entity The entity
|
||||
* @param modelPath The model path for the model to back the actor
|
||||
*/
|
||||
public static void makeEntityDrawablePreexistingModel(Entity entity, String modelPath){
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
|
||||
@ -23,12 +23,23 @@ public class EntityDataStrings {
|
||||
public static final String DRAW_OUTLINE = "drawOutline";
|
||||
public static final String INSTANCED_ACTOR = "instancedActor";
|
||||
public static final String DRAW_INSTANCED = "drawInstanced";
|
||||
|
||||
|
||||
/*
|
||||
Instanced Entity
|
||||
*/
|
||||
public static final String INSTANCED_MODEL_ATTRIBUTE = "instancedModelAttribute";
|
||||
|
||||
|
||||
/*
|
||||
Terrain Entity
|
||||
*/
|
||||
public static final String TERRAIN_IS_TERRAIN = "terrainEntity";
|
||||
|
||||
/*
|
||||
* Fluid Entity
|
||||
*/
|
||||
public static final String FLUID_IS_FLUID = "fluidEntity";
|
||||
|
||||
|
||||
/*
|
||||
@ -111,6 +122,7 @@ public class EntityDataStrings {
|
||||
public static final String PHYSICS_MODEL_TEMPLATE = "physicsModelTemplate";
|
||||
public static final String PHYSICS_MASS = "physicsMass";
|
||||
public static final String PHYSICS_INVERSE_INERTIA_TENSOR = "physicsInverseInertiaTensor";
|
||||
public static final String PHYSICS_ENGINE_AUTHORITATIVE_TRANSFORM = "physicsEngineAuthoritativeTransform"; // The physics engine is authoritative abound transforms of object (eg position, rotation)
|
||||
|
||||
/*
|
||||
Gravity Entity
|
||||
@ -154,9 +166,12 @@ public class EntityDataStrings {
|
||||
*/
|
||||
public static final String ATTACH_ENTITY_IS_ATTACHED = "attachIsAttached";
|
||||
public static final String ATTACH_PARENT = "attachParent";
|
||||
public static final String ATTACH_TARGET_BONE = "attachTargetBone";
|
||||
public static final String ATTACH_TARGET_BONE = "attachTargetBone"; //Attaches to a specific bone of the entity
|
||||
public static final String ATTACH_TARGET_BASE = "attachTargetBase"; //Attaches to the base of the entity (should be the same as getPosition(entity))
|
||||
public static final String ATTACH_CHILDREN_LIST = "attachChildrenList";
|
||||
public static final String ATTACH_ROTATION_OFFSET = "attachRotationOffset";
|
||||
public static final String ATTACH_POSITION_OFFSET = "attachPositionOffset";
|
||||
public static final String ATTACH_TRANSFORM = "attachTransform";
|
||||
|
||||
/*
|
||||
Item Entity
|
||||
@ -223,6 +238,7 @@ public class EntityDataStrings {
|
||||
*/
|
||||
public static final String FOLIAGE_IS_FOLIAGE = "foliageIsFoliage";
|
||||
public static final String FOLIAGE_TYPE = "foliageType";
|
||||
public static final String FOLIAGE_AMBIENT_TREE = "foliageAmbientTree";
|
||||
|
||||
/*
|
||||
Equip state
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.entity;
|
||||
public class EntityTags {
|
||||
|
||||
public static final String BONE_ATTACHED = "boneAttached";
|
||||
public static final String TRANSFORM_ATTACHED = "transformAttached";
|
||||
public static final String COLLIDABLE = "collidable";
|
||||
public static final String SPRINTABLE = "sprintable";
|
||||
public static final String MOVEABLE = "moveable";
|
||||
@ -12,7 +13,8 @@ public class EntityTags {
|
||||
public static final String CREATURE = "creature";
|
||||
public static final String UI = "ui";
|
||||
public static final String DRAWABLE = "drawable";
|
||||
public static final String DRAW_INSTANCED = "drawInstanced";
|
||||
public static final String DRAW_INSTANCED = "drawInstanced"; //if it's instanced, but not necessarily managed by a service (ie a tree branch)
|
||||
public static final String DRAW_INSTANCED_MANAGED = "drawInstancedManaged"; //if it's managed by a service (ie foliage manager)
|
||||
public static final String LIGHT = "light";
|
||||
public static final String ITEM = "item";
|
||||
public static final String GRAVITY = "gravity";
|
||||
|
||||
@ -19,6 +19,7 @@ import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
||||
import electrosphere.server.datacell.utils.EntityLookupUtils;
|
||||
import electrosphere.server.poseactor.PoseActor;
|
||||
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
@ -33,8 +34,8 @@ public class EntityUtils {
|
||||
return (Vector3d)e.getData(EntityDataStrings.DATA_STRING_POSITION);
|
||||
}
|
||||
|
||||
public static Quaternionf getRotation(Entity e){
|
||||
return (Quaternionf)e.getData(EntityDataStrings.DATA_STRING_ROTATION);
|
||||
public static Quaterniond getRotation(Entity e){
|
||||
return (Quaterniond)e.getData(EntityDataStrings.DATA_STRING_ROTATION);
|
||||
}
|
||||
|
||||
public static Vector3f getScale(Entity e){
|
||||
@ -55,7 +56,7 @@ public class EntityUtils {
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath));
|
||||
// rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
@ -73,7 +74,7 @@ public class EntityUtils {
|
||||
Entity rVal = new Entity();
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0)));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, new Vector3d(1,0,0)));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
@ -92,7 +93,7 @@ public class EntityUtils {
|
||||
rVal.putData(EntityDataStrings.POSE_ACTOR, new PoseActor(modelPath));
|
||||
// rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
|
||||
rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
|
||||
@ -103,7 +104,7 @@ public class EntityUtils {
|
||||
Entity rVal = new Entity();
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0)));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, new Vector3d(1,0,0)));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_UI_ELEMENT, true);
|
||||
Globals.clientScene.registerEntity(rVal);
|
||||
@ -119,7 +120,7 @@ public class EntityUtils {
|
||||
Entity rVal = new Entity();
|
||||
// rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
|
||||
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
|
||||
EntityLookupUtils.registerServerEntity(rVal);
|
||||
return rVal;
|
||||
@ -136,6 +137,11 @@ public class EntityUtils {
|
||||
if(Globals.realmManager != null){
|
||||
Realm realm = Globals.realmManager.getEntityRealm(e);
|
||||
if(realm != null){
|
||||
//get data cell
|
||||
ServerDataCell dataCell = realm.getEntityDataCellMapper().getEntityDataCell(e);
|
||||
if(dataCell != null){
|
||||
dataCell.getScene().deregisterEntity(e);
|
||||
}
|
||||
realm.getEntityDataCellMapper().ejectEntity(e);
|
||||
}
|
||||
Globals.realmManager.removeEntity(e);
|
||||
|
||||
@ -45,6 +45,7 @@ public class Scene {
|
||||
tagEntityMap.put(EntityTags.ITEM, new HashSet<Entity>());
|
||||
tagEntityMap.put(EntityTags.GRAVITY, new HashSet<Entity>());
|
||||
tagEntityMap.put(EntityTags.PARTICLE, new HashSet<Entity>());
|
||||
tagEntityMap.put(EntityTags.TRANSFORM_ATTACHED, new HashSet<Entity>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -33,7 +33,7 @@ public class ServerEntityUtils {
|
||||
realm.initializeServerSideEntity(entity, cell);
|
||||
}
|
||||
//reposition entity
|
||||
CollisionObjUtils.positionCharacter(entity, position);
|
||||
CollisionObjUtils.serverPositionCharacter(entity, position);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,9 +44,9 @@ public class ServerEntityUtils {
|
||||
public static void repositionEntity(Entity entity, Vector3d position){
|
||||
Realm realm = Globals.realmManager.getEntityRealm(entity);
|
||||
//if server, get current server data cell
|
||||
if(Globals.RUN_SERVER){
|
||||
ServerDataCell oldDataCell = realm.getDataCellManager().getDataCellAtPoint(EntityUtils.getPosition(entity));
|
||||
ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position);
|
||||
ServerDataCell oldDataCell = realm.getDataCellManager().getDataCellAtPoint(EntityUtils.getPosition(entity));
|
||||
ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position);
|
||||
if(oldDataCell != newDataCell){
|
||||
if(newDataCell != null){
|
||||
ServerDataCell.moveEntityFromCellToCell(entity, oldDataCell, newDataCell);
|
||||
ServerBehaviorTreeUtils.updateCell(entity, oldDataCell);
|
||||
@ -58,15 +58,15 @@ public class ServerEntityUtils {
|
||||
ServerBehaviorTreeUtils.updateCell(entity, oldDataCell);
|
||||
}
|
||||
}
|
||||
// //if the server is also a client, update the drawcell manager to know to pull new chunks
|
||||
// if(Globals.RUN_CLIENT){
|
||||
// Globals.drawCellManager.invalidateAllCells();
|
||||
// Globals.drawCellManager.setCellX(Globals.clientPlayerData.getWorldPos().x);
|
||||
// Globals.drawCellManager.setCellY(Globals.clientPlayerData.getWorldPos().z);
|
||||
// }
|
||||
}
|
||||
// //if the server is also a client, update the drawcell manager to know to pull new chunks
|
||||
// if(Globals.RUN_CLIENT){
|
||||
// Globals.drawCellManager.invalidateAllCells();
|
||||
// Globals.drawCellManager.setCellX(Globals.clientPlayerData.getWorldPos().x);
|
||||
// Globals.drawCellManager.setCellY(Globals.clientPlayerData.getWorldPos().z);
|
||||
// }
|
||||
//reposition entity
|
||||
CollisionObjUtils.positionCharacter(entity, Globals.spawnPoint);
|
||||
CollisionObjUtils.serverPositionCharacter(entity, Globals.spawnPoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package electrosphere.entity.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
/**
|
||||
* A field in a synchronized behavior tree that is synchronized between the server and the client.
|
||||
*/
|
||||
public @interface SyncedField {
|
||||
|
||||
//True if the field is an enum
|
||||
boolean isEnum() default false;
|
||||
|
||||
//if the field is an enum, this value is MANDETORY. Determines what id will be set to when serialized/deserialized
|
||||
int enumId() default 0;
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package electrosphere.entity.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
/**
|
||||
* A behavior tree that will have modifications by code generation.
|
||||
* It is synchronized between the server and client with auto-generated netcode.
|
||||
*/
|
||||
public @interface SynchronizedBehaviorTree {
|
||||
|
||||
//The name of the behavior tree
|
||||
public String name() default "";
|
||||
|
||||
//True if this is a server-side behavior tree
|
||||
public boolean isServer() default false;
|
||||
|
||||
}
|
||||
@ -1,7 +1,14 @@
|
||||
package electrosphere.entity.state;
|
||||
|
||||
/**
|
||||
* A behavior tree
|
||||
*/
|
||||
public interface BehaviorTree {
|
||||
|
||||
/**
|
||||
* Simulates the behavior tree
|
||||
* @param deltaTime The time since the last call to the simulate function
|
||||
*/
|
||||
public void simulate(float deltaTime);
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
package electrosphere.entity.state;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface BehaviorTreeAnnotation {
|
||||
|
||||
public String name() default "";
|
||||
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package electrosphere.entity.state;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SyncedField {
|
||||
|
||||
boolean isEnum() default false;
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.entity.state.attack;
|
||||
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.Main;
|
||||
import electrosphere.entity.Entity;
|
||||
@ -17,7 +18,6 @@ import electrosphere.entity.types.creature.CreatureUtils;
|
||||
import electrosphere.entity.types.hitbox.HitboxUtils;
|
||||
import electrosphere.entity.types.item.ItemUtils;
|
||||
import electrosphere.entity.types.projectile.ProjectileUtils;
|
||||
import electrosphere.game.collision.collidable.Collidable;
|
||||
import electrosphere.game.data.creature.type.attack.AttackMove;
|
||||
import electrosphere.game.data.creature.type.equip.EquipPoint;
|
||||
import electrosphere.net.parser.net.message.EntityMessage;
|
||||
@ -128,7 +128,7 @@ public class AttackTree implements BehaviorTree {
|
||||
}
|
||||
}
|
||||
Vector3d movementVector = CreatureUtils.getFacingVector(parent);
|
||||
EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z));
|
||||
EntityUtils.getRotation(parent).rotationTo(new Vector3d(0,0,1), new Vector3d(movementVector.x,movementVector.y,movementVector.z));
|
||||
//set initial stuff
|
||||
state = AttackTreeState.WINDUP;
|
||||
frameCurrent = 0;
|
||||
@ -297,7 +297,7 @@ public class AttackTree implements BehaviorTree {
|
||||
//spawn projectile
|
||||
//TODO: solve spawnPosition, initialVector
|
||||
Vector3d spawnPosition = new Vector3d(0,0,0);
|
||||
Quaternionf arrowRotation = new Quaternionf();
|
||||
Quaterniond arrowRotation = new Quaterniond();
|
||||
String targetBone = null;
|
||||
EquipState equipState = EquipState.getEquipState(parent);
|
||||
EquipPoint weaponPoint = null;
|
||||
@ -309,7 +309,7 @@ public class AttackTree implements BehaviorTree {
|
||||
//transform bone space
|
||||
spawnPosition = new Vector3d(parentActor.getBonePosition(targetBone));
|
||||
spawnPosition = spawnPosition.mul(((Vector3f)EntityUtils.getScale(parent)));
|
||||
Quaternionf rotation = EntityUtils.getRotation(parent);
|
||||
Quaterniond rotation = EntityUtils.getRotation(parent);
|
||||
spawnPosition = spawnPosition.rotate(new Quaterniond(rotation.x,rotation.y,rotation.z,rotation.w));
|
||||
//transform worldspace
|
||||
spawnPosition.add(new Vector3d(EntityUtils.getPosition(parent)));
|
||||
|
||||