fluids are rendering albeit poorly
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-03-17 18:44:39 -04:00
parent f328d16bab
commit 97edfacd41
35 changed files with 1974 additions and 255 deletions

View 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;
}

View 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;
}

View File

@ -3,7 +3,7 @@
```
generate 200 x 200
interpolate x 20 in each direction
this map will be 0.25 km resolution
this map will be 0.25 km resolution and ~62mb in size
Useful for macro sim
```

View File

@ -196,6 +196,10 @@ Transvoxel Algorithm
- Prebake all textures into atlas
- Rewrite marching cubes shader to leverage this atlas
Another pass at grass
- Fix shader being camera position independent (if you move the wind moves with you lol)
- Multiple foliage models in same cell
Build a lod system
- Could potentially be held at actor level
- Link different models based on LOD level

View File

@ -184,6 +184,23 @@
"worldZ",
"chunkData"
]
},
{
"messageName" : "RequestFluidData",
"data" : [
"worldX",
"worldY",
"worldZ"
]
},
{
"messageName" : "sendFluidData",
"data" : [
"worldX",
"worldY",
"worldZ",
"chunkData"
]
}
]
}

View File

@ -11,6 +11,7 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.Scene;
import electrosphere.entity.types.attach.AttachUtils;
@Deprecated
public class ClientEntityCullingManager {
Scene scene;
@ -40,38 +41,4 @@ public class ClientEntityCullingManager {
target.putData(EntityDataStrings.DATA_STRING_DRAW, true);
}
public void clearOutOfBoundsEntities(){
if(Globals.commonWorldData != null && Globals.playerEntity != null && Globals.clientPlayerData != null){
Vector3d playerCharacterPos = EntityUtils.getPosition(Globals.playerEntity);
int playerCharacterWorldX = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.x);
int playerCharacterWorldY = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.y);
int playerCharacterWorldZ = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.z);
if(
playerCharacterWorldX != Globals.clientPlayerData.getWorldPos().x||
playerCharacterWorldY != Globals.clientPlayerData.getWorldPos().y ||
playerCharacterWorldZ != Globals.clientPlayerData.getWorldPos().z
){
for(Entity entity : scene.getEntityList()){
if(entity.containsKey(EntityDataStrings.TERRAIN_IS_TERRAIN) || entity.containsKey(EntityDataStrings.ATTACH_PARENT) || entity.containsKey(EntityDataStrings.COLLISION_ENTITY_PARENT)){
} else {
Vector3d position = EntityUtils.getPosition(entity);
//common world data is initialized with the collision data
//if this is null then the engine hasn't fully started up yet
if(position != null){
int worldX = Globals.clientWorldData.convertRealToWorld(position.x);
int worldZ = Globals.clientWorldData.convertRealToWorld(position.z);
if(!Globals.drawCellManager.coordsInPhysicsSpace(worldX, worldZ)){
//we need to just hide the entity
recursiveHide(entity);
} else {
//if the entity is within range and it's not set to visible, make it visible
recursiveShow(entity);
}
}
}
}
}
}
}
}

View File

@ -17,48 +17,19 @@ public class FluidChunkData {
//The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk
public static final int CHUNK_DATA_GENERATOR_SIZE = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE;
//What type of fluid is in this voxel, eg stone vs dirt vs grass, etc
int[][][] voxelType;
//How much of that fluid type is in this voxel
float[][][] voxelWeight;
//The velocities
float[][][] velocityX;
float[][][] velocityY;
float[][][] velocityZ;
//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
@ -91,23 +62,23 @@ public class FluidChunkData {
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);
}
}
// /**
// * 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
@ -119,12 +90,11 @@ public class FluidChunkData {
}
/**
* Gets the type of a voxel at a position
* @param localPosition The local position
* @return The type of the specified voxel
* Gets the weight of a voxel at a poisiton
* @return The weight of the specified voxel
*/
public int getType(Vector3i localPosition){
return voxelType[localPosition.x][localPosition.y][localPosition.z];
public float getWeight(int x, int y, int z){
return voxelWeight[z][y][z];
}
/**

View File

@ -0,0 +1,240 @@
package electrosphere.client.fluid.cells;
import org.joml.Vector3d;
import org.joml.Vector3i;
import org.ode4j.ode.DBody;
import electrosphere.client.fluid.cache.FluidChunkData;
import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.fluid.FluidChunk;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.renderer.texture.Texture;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/**
*
* @author satellite
*/
public class FluidCell {
//the position of the draw cell in world coordinates
Vector3i worldPos;
FluidChunkData data;
Entity modelEntity;
ShaderProgram program;
DBody physicsObject;
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
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");
}
FluidCell(){
}
/**
* Constructs a drawcell object
*/
public static FluidCell generateFluidCell(
Vector3i worldPos,
FluidChunkData data,
ShaderProgram program
){
FluidCell rVal = new FluidCell();
rVal.worldPos = worldPos;
rVal.program = program;
rVal.data = data;
return rVal;
}
/**
* Generates a drawable entity based on this chunk
*/
public void generateDrawableEntity(){
if(modelEntity != null){
Globals.clientScene.deregisterEntity(modelEntity);
}
fillInData();
modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight());
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;
}
/**
* Fills in the internal arrays of data for generate terrain models
*/
private void fillInData(){
//
//fill in data
//
//main chunk
FluidChunkData currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos);
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
weights[x][y][z] = currentChunk.getWeight(x,y,z);
}
}
}
//face X
if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = currentChunk.getWeight(0, i, j);
}
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = -1;
}
}
}
//face Y
if(worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = currentChunk.getWeight(i, 0, j);
}
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = -1;
}
}
}
//face Z
if(worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z + 1);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, j, 0);
}
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
}
}
}
//edge X-Y
if(
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = currentChunk.getWeight(0, 0, i);
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = -1;
}
}
//edge X-Z
if(
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z + 1);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, i, 0);
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
}
}
//edge Y-Z
if(
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z + 1);
if(currentChunk != null){
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, 0, 0);
}
}
} else {
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
}
}
if(
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
if(currentChunk != null){
if(currentChunk != null){
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, 0, 0);
}
}
} else {
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
}
}
}

View File

@ -19,7 +19,7 @@ import electrosphere.renderer.shader.ShaderProgram;
*
* @author satellite
*/
public class FluidDrawCellManager {
public class FluidCellManager {
//the center of this cell manager's array in cell space
@ -35,8 +35,8 @@ public class FluidDrawCellManager {
int miniCellWidth;
//all currently displaying mini cells
Set<FluidDrawCell> cells;
Map<String,FluidDrawCell> keyCellMap = new HashMap<String,FluidDrawCell>();
Set<FluidCell> cells;
Map<String,FluidCell> keyCellMap = new HashMap<String,FluidCell>();
Set<String> hasNotRequested;
Set<String> hasRequested;
Set<String> drawable;
@ -59,8 +59,6 @@ public class FluidDrawCellManager {
int worldBoundDiscreteMin = 0;
int worldBoundDiscreteMax = 0;
//client terrain manager
// ClientTerrainManager clientTerrainManager;
//ready to start updating?
@ -78,9 +76,9 @@ public class FluidDrawCellManager {
* @param discreteX The initial discrete position X coordinate
* @param discreteY The initial discrete position Y coordinate
*/
public FluidDrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
public FluidCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f);
cells = new HashSet<FluidDrawCell>();
cells = new HashSet<FluidCell>();
hasNotRequested = new HashSet<String>();
drawable = new HashSet<String>();
undrawable = new HashSet<String>();
@ -102,7 +100,7 @@ public class FluidDrawCellManager {
update = true;
}
FluidDrawCellManager(){
FluidCellManager(){
}
@ -132,7 +130,7 @@ public class FluidDrawCellManager {
){
// if(!hasRequested.contains(targetKey)){
//client should request chunk data from server
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestFluidDataMessage(
worldPos.x,
worldPos.y,
worldPos.z
@ -161,7 +159,7 @@ public class FluidDrawCellManager {
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
){
if(containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){
FluidDrawCell cell = FluidDrawCell.generateFluidCell(
FluidCell cell = FluidCell.generateFluidCell(
worldPos,
Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z),
program
@ -256,14 +254,14 @@ public class FluidDrawCellManager {
* Clears all cells outside of draw radius
*/
private void clearOutOfBoundsCells(){
Set<FluidDrawCell> cellsToRemove = new HashSet<FluidDrawCell>();
for(FluidDrawCell cell : cells){
Set<FluidCell> cellsToRemove = new HashSet<FluidCell>();
for(FluidCell cell : cells){
Vector3d realPos = cell.getRealPos();
if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){
cellsToRemove.add(cell);
}
}
for(FluidDrawCell cell : cellsToRemove){
for(FluidCell cell : cellsToRemove){
cells.remove(cell);
String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z);
hasNotRequested.remove(key);
@ -358,8 +356,8 @@ public class FluidDrawCellManager {
}
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
if(Globals.clientTerrainManager != null){
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
if(Globals.clientFluidManager != null){
return Globals.clientFluidManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
}
return true;
}

View File

@ -1,103 +0,0 @@
package electrosphere.client.fluid.cells;
import org.joml.Vector3d;
import org.joml.Vector3i;
import org.ode4j.ode.DBody;
import electrosphere.client.fluid.cache.FluidChunkData;
import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.fluid.FluidChunk;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.renderer.texture.Texture;
/**
*
* @author satellite
*/
public class FluidDrawCell {
//the position of the draw cell in world coordinates
Vector3i worldPos;
FluidChunkData data;
Entity modelEntity;
ShaderProgram program;
DBody physicsObject;
static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png");
static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png");
static Texture groundTextureThree = new Texture("/Textures/Ground/Dirt1.png");
static Texture groundTextureFour = new Texture("/Textures/Ground/Dirt1.png");
static {
// groundTextureOne = new Texture("/Textures/Ground/GrassTileable.png");
// groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png");
// groundTextureThree = new Texture("/Textures/Ground/Dirt1.png");
// groundTextureFour = new Texture("/Textures/Ground/Dirt1.png");
}
FluidDrawCell(){
}
/**
* Constructs a drawcell object
*/
public static FluidDrawCell generateFluidCell(
Vector3i worldPos,
FluidChunkData data,
ShaderProgram program
){
FluidDrawCell rVal = new FluidDrawCell();
rVal.worldPos = worldPos;
rVal.program = program;
rVal.data = data;
return rVal;
}
/**
* Generates a drawable entity based on this chunk
*/
public void generateDrawableEntity(){
if(modelEntity != null){
Globals.clientScene.deregisterEntity(modelEntity);
}
modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight(), data.getVoxelType());
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
}
protected Vector3d getRealPos(){
return new Vector3d(
worldPos.x * FluidChunkData.CHUNK_SIZE,
worldPos.y * FluidChunkData.CHUNK_SIZE,
worldPos.z * FluidChunkData.CHUNK_SIZE
);
}
/**
* Destroys a drawcell including its physics
*/
public void destroy(){
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
collisionEngine.destroyEntityThatHasPhysics(modelEntity);
EntityUtils.cleanUpEntity(modelEntity);
}
/**
* Gets the current chunk data for this draw cell
* @return The chunk data
*/
public FluidChunkData getData(){
return data;
}
}

View File

@ -57,29 +57,42 @@ public class ClientFluidManager {
for(TerrainMessage message : messageQueue){
messageQueue.remove(message);
switch(message.getMessageSubtype()){
case SENDCHUNKDATA: {
int[][][] values = new int[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE];
float[][][] weights = new float[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE];
case SENDFLUIDDATA: {
float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
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++){
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
weights[x][y][z] = floatBuffer.get();
}
}
}
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();
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
velocityX[x][y][z] = floatBuffer.get();
}
}
}
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
velocityY[x][y][z] = floatBuffer.get();
}
}
}
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
velocityZ[x][y][z] = floatBuffer.get();
}
}
}
FluidChunkData data = new FluidChunkData();
data.setVoxelType(values);
data.setVoxelWeight(weights);
fluidCache.addChunkDataToCache(
message.getworldX(), message.getworldY(), message.getworldZ(),
@ -96,7 +109,7 @@ public class ClientFluidManager {
}
}
public void attachTerrainMessage(TerrainMessage message){
public void attachFluidMessage(TerrainMessage message){
messageQueue.add(message);
}

View File

@ -311,7 +311,6 @@ public class ClientFoliageManager {
* @param voxelPos The voxel position
*/
private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel){
System.out.println("Create foliage cell");
//get foliage types supported
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPos)).getAmbientFoliage();
@ -350,11 +349,6 @@ public class ClientFoliageManager {
float height_20 = (float)(sample_20 != null ? sample_20.y : height_11);
float height_21 = (float)(sample_21 != null ? sample_21.y : height_11);
float height_22 = (float)(sample_22 != null ? sample_22.y : height_11);
if(worldPos.x == 0 && worldPos.z == 0 && voxelPos.x < 5 && voxelPos.z < 5){
System.out.println(voxelPos);
System.out.println(height_11);
System.out.println(realPos);
}
//each height is in real world coordinates that are absolute
//when rendering, there's already a y offset for the center of the field of grass (based on the model matrix)
//so when offseting the position of the blade of grass RELATIVE to the overall instance being drawn, need to subtract the real world coordinates of the overall instance
@ -375,15 +369,6 @@ public class ClientFoliageManager {
double offsetZ = relativePositionOnGridZ;
//determine quadrant we're placing in
double offsetY = 0;
if(worldPos.x == 0 && worldPos.z == 0 && voxelPos.x < 5 && voxelPos.z < 5 && x == 26 && z == 26){
offsetY =
height_11 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_12 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
height_21 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_22 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
System.out.println(offsetY);
System.out.println((float)offsetY - (float)realPos.y);
}
boolean addBlade = false;
if(relativePositionOnGridX >=0){
if(relativePositionOnGridZ >= 0){

View File

@ -1,5 +1,6 @@
package electrosphere.client.sim;
import electrosphere.client.fluid.manager.ClientFluidManager;
import electrosphere.client.instancing.InstanceUpdater;
import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.engine.Globals;
@ -27,6 +28,7 @@ public class ClientFunctions {
public static void runClientFunctions(){
ClientTerrainManager.generateTerrainChunkGeometry();
ClientFluidManager.generateFluidChunkGeometry();
updateSkyboxPos();
Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange();
InstanceUpdater.updateInstancedActorPriority();
@ -48,11 +50,15 @@ public class ClientFunctions {
public static void loadTerrain(){
if(Globals.clientTerrainManager != null){
Globals.clientTerrainManager.handleMessages();
updateCellManager();
updateTerrainCellManager();
}
if(Globals.clientFluidManager != null){
Globals.clientFluidManager.handleMessages();
updateFluidCellManager();
}
}
static void updateCellManager(){
static void updateTerrainCellManager(){
///
/// C L I E N T C E L L M A N A G E R
///
@ -65,4 +71,15 @@ public class ClientFunctions {
Globals.drawCellManager.update();
}
}
static void updateFluidCellManager(){
//fluid work
if(Globals.fluidCellManager != null && Globals.clientWorldData != null){
if(Globals.playerEntity != null){
newPlayerCharacterPosition = EntityUtils.getPosition(Globals.playerEntity);
}
Globals.fluidCellManager.calculateDeltas(newPlayerCharacterPosition);
Globals.fluidCellManager.update();
}
}
}

View File

@ -12,6 +12,7 @@ import electrosphere.audio.AudioEngine;
import electrosphere.audio.VirtualAudioSourceManager;
import electrosphere.auth.AuthenticationManager;
import electrosphere.client.culling.ClientEntityCullingManager;
import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.fluid.manager.ClientFluidManager;
import electrosphere.client.foliagemanager.ClientFoliageManager;
import electrosphere.client.player.ClientPlayerData;
@ -52,6 +53,7 @@ import electrosphere.renderer.actor.instance.InstanceManager;
import electrosphere.renderer.light.PointLight;
import electrosphere.renderer.light.SpotLight;
import electrosphere.renderer.loading.ModelPretransforms;
import electrosphere.renderer.meshgen.FluidChunkModelGeneration;
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
import electrosphere.renderer.model.Material;
import electrosphere.renderer.shader.ShaderOptionMap;
@ -66,6 +68,7 @@ import electrosphere.server.content.ServerContentManager;
import electrosphere.server.datacell.EntityDataCellMapper;
import electrosphere.server.datacell.RealmManager;
import electrosphere.server.db.DatabaseController;
import electrosphere.server.fluid.manager.ServerFluidManager;
import electrosphere.server.pathfinding.NavMeshManager;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation;
@ -254,6 +257,12 @@ public class Globals {
//terrain manager
// public static boolean LOAD_TERRAIN = true;
public static ServerTerrainManager serverTerrainManager;
//fluid manager
public static ServerFluidManager serverFluidManager;
//spawn point
public static Vector3d spawnPoint = new Vector3d(0,0,0);
//content manager
@ -304,8 +313,11 @@ public class Globals {
public static ClientPlayerData clientPlayerData = new ClientPlayerData();
//chunk stuff
//constant for how far in game units you have to move to load chunks
//draw cell manager
public static DrawCellManager drawCellManager;
//fluid cell manager
public static FluidCellManager fluidCellManager;
//navmesh manager
public static NavMeshManager navMeshManager;
@ -413,6 +425,8 @@ public class Globals {
navMeshManager = new NavMeshManager();
//terrain
Globals.clientTerrainManager = new ClientTerrainManager();
//fluid
Globals.clientFluidManager = new ClientFluidManager();
//game config
gameConfigDefault = electrosphere.game.data.Config.loadDefaultConfig();
gameConfigCurrent = gameConfigDefault;
@ -471,8 +485,7 @@ public class Globals {
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
//init fluid shader program
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs");
TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs");
FluidChunkModelGeneration.fluidChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid2/fluid2.vs", "/Shaders/fluid2/fluid2.fs");
//init models
assetManager.addModelPathToQueue("Models/unitsphere.fbx");
assetManager.addModelPathToQueue("Models/unitsphere_1.fbx");

View File

@ -15,6 +15,7 @@ import electrosphere.entity.ServerEntityUtils;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.content.ServerContentManager;
import electrosphere.server.fluid.manager.ServerFluidManager;
import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils;
@ -56,6 +57,7 @@ public class ArenaLoading {
private static void initServerArenaTerrainManager(){
Globals.serverTerrainManager = ServerTerrainManager.constructArenaTerrainManager();
Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager();
}
private static void initServerArenaWorldData(){

View File

@ -11,6 +11,7 @@ import electrosphere.audio.AudioUtils;
import electrosphere.audio.VirtualAudioSource;
import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
import electrosphere.client.culling.ClientEntityCullingManager;
import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.foliagemanager.ClientFoliageManager;
import electrosphere.client.sim.ClientSimulation;
import electrosphere.client.targeting.crosshair.Crosshair;
@ -93,6 +94,8 @@ public class ClientLoading {
initFoliageManager();
//initialize the cell manager (client)
initDrawCellManager();
//init the fluid cell manager
initFluidCellManager();
//initialize the basic graphical entities of the world (skybox, camera)
initWorldBaseGraphicalEntities();
//init arena specific stuff (ie different skybox colors)
@ -290,6 +293,41 @@ public class ClientLoading {
// System.out.println("Draw Cell Manager ready");
}
static void initFluidCellManager(){
while(Globals.clientWorldData == null){
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) {
}
}
//initialize draw cell manager
Globals.fluidCellManager = new FluidCellManager(Globals.clientTerrainManager, 0, 0, 0);
//set our draw cell manager to actually generate drawable chunks
Globals.fluidCellManager.setGenerateDrawables(true);
//Alerts the client simulation that it should start loading terrain
Globals.clientSimulation.setLoadingTerrain(true);
//wait for all the terrain data to arrive
while(Globals.fluidCellManager.containsUnrequestedCell()){
// Globals.drawCellManager.updateInvalidCell();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// System.out.println("invalid cell");
}
while(Globals.fluidCellManager.containsUndrawableCell()){
// Globals.drawCellManager.makeCellDrawable();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// System.out.println("undrawable");
}
}
/**
* Starts up the entity culling manager
*/

View File

@ -17,9 +17,9 @@ public class FluidChunk {
* @param values The values (block types)
* @return The fluid chunk entity
*/
public static Entity clientCreateFluidChunkEntity(float[][][] weights, int[][][] values){
public static Entity clientCreateFluidChunkEntity(float[][][] weights){
FluidChunkModelData data = FluidChunkModelGeneration.generateFluidChunkData(weights, values);
FluidChunkModelData data = FluidChunkModelGeneration.generateFluidChunkData(weights);
String modelPath = ClientFluidManager.queueFluidGridGeneration(data);
Entity rVal = EntityCreationUtils.createClientSpatialEntity();

View File

@ -22,7 +22,7 @@ public class LoggerInterface {
public static void initLoggers(){
loggerStartup = new Logger(LogLevel.WARNING);
loggerNetworking = new Logger(LogLevel.WARNING);
loggerNetworking = new Logger(LogLevel.DEBUG);
loggerFileIO = new Logger(LogLevel.WARNING);
loggerGameLogic = new Logger(LogLevel.WARNING);
loggerRenderer = new Logger(LogLevel.WARNING);

View File

@ -3,6 +3,7 @@ package electrosphere.net.client.protocol;
import org.joml.Vector3f;
import electrosphere.client.fluid.cache.FluidChunkData;
import electrosphere.client.scene.ClientWorldData;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.collision.CollisionWorldData;
@ -49,6 +50,9 @@ public class TerrainProtocol {
}
}
} break;
case SENDFLUIDDATA: {
Globals.clientFluidManager.attachFluidMessage(message);
} break;
default:
LoggerInterface.loggerNetworking.WARNING("Client networking: Unhandled message of type: " + message.getMessageSubtype());
break;

View File

@ -236,6 +236,16 @@ SYNCHRONIZATION_MESSAGE,
rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer);
}
break;
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer);
}
break;
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer);
}
break;
}
break;
case TypeBytes.MESSAGE_TYPE_SERVER:

View File

@ -17,6 +17,8 @@ public class TerrainMessage extends NetworkMessage {
SPAWNPOSITION,
REQUESTCHUNKDATA,
SENDCHUNKDATA,
REQUESTFLUIDDATA,
SENDFLUIDDATA,
}
TerrainMessageType messageType;
@ -266,6 +268,14 @@ public class TerrainMessage extends NetworkMessage {
}
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
return TerrainMessage.canParsesendChunkDataMessage(byteBuffer);
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE){
return true;
} else {
return false;
}
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
return TerrainMessage.canParsesendFluidDataMessage(byteBuffer);
}
return false;
}
@ -488,6 +498,72 @@ public class TerrainMessage extends NetworkMessage {
return rVal;
}
public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
stripPacketHeader(byteBuffer);
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
return rVal;
}
public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
rVal.setworldX(worldX);
rVal.setworldY(worldY);
rVal.setworldZ(worldZ);
rVal.serialize();
return rVal;
}
public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList();
if(currentStreamLength < 6){
return false;
}
if(currentStreamLength < 10){
return false;
}
if(currentStreamLength < 14){
return false;
}
int chunkDataSize = 0;
if(currentStreamLength < 18){
return false;
} else {
temporaryByteQueue.add(byteBuffer.peek(14 + 0));
temporaryByteQueue.add(byteBuffer.peek(14 + 1));
temporaryByteQueue.add(byteBuffer.peek(14 + 2));
temporaryByteQueue.add(byteBuffer.peek(14 + 3));
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
}
if(currentStreamLength < 18 + chunkDataSize){
return false;
}
return true;
}
public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
stripPacketHeader(byteBuffer);
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
return rVal;
}
public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
rVal.setworldX(worldX);
rVal.setworldY(worldY);
rVal.setworldZ(worldZ);
rVal.setchunkData(chunkData);
rVal.serialize();
return rVal;
}
@Override
void serialize(){
byte[] intValues = new byte[8];
@ -718,6 +794,51 @@ public class TerrainMessage extends NetworkMessage {
rawBytes[18+i] = chunkData[i];
}
break;
case REQUESTFLUIDDATA:
rawBytes = new byte[2+4+4+4];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
//entity messaage header
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA;
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
for(int i = 0; i < 4; i++){
rawBytes[6+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
for(int i = 0; i < 4; i++){
rawBytes[10+i] = intValues[i];
}
break;
case SENDFLUIDDATA:
rawBytes = new byte[2+4+4+4+4+chunkData.length];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
//entity messaage header
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA;
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
for(int i = 0; i < 4; i++){
rawBytes[6+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
for(int i = 0; i < 4; i++){
rawBytes[10+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
for(int i = 0; i < 4; i++){
rawBytes[14+i] = intValues[i];
}
for(int i = 0; i < chunkData.length; i++){
rawBytes[18+i] = chunkData[i];
}
break;
}
serialized = true;
}

View File

@ -88,6 +88,8 @@ Message categories
public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION = 6;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA = 7;
public static final byte TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA = 8;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 9;
public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 10;
/*
Terrain packet sizes
*/
@ -99,6 +101,7 @@ Message categories
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE_SIZE = 38;
public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION_SIZE = 26;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE = 14;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE = 14;
/*
Server subcategories
*/

View File

@ -1,7 +1,6 @@
package electrosphere.net.parser.net.raw;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.NetworkMessage;
package electrosphere.net.parser.net.raw;
import electrosphere.net.parser.net.message.NetworkMessage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -52,7 +51,7 @@ public class NetworkParser {
}
} catch (IOException ex) {
ex.printStackTrace();
LoggerInterface.loggerNetworking.ERROR("", ex);
System.exit(0);
}
}

View File

@ -13,6 +13,7 @@ import electrosphere.net.server.Server;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player;
import electrosphere.server.datacell.Realm;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.terrain.editing.TerrainEditing;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModification;
@ -38,11 +39,17 @@ public class TerrainProtocol {
case REQUESTUSETERRAINPALETTE: {
attemptUseTerrainEditPalette(connectionHandler, message);
} break;
case REQUESTFLUIDDATA: {
sendWorldFluidSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
);
} break;
//all ignored message types
case RESPONSEMETADATA:
case SPAWNPOSITION:
case UPDATEVOXEL:
case SENDCHUNKDATA:
case SENDFLUIDDATA:
//silently ignore
break;
}
@ -194,6 +201,64 @@ public class TerrainProtocol {
// }
}
static void sendWorldFluidSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
ServerFluidChunk chunk = Globals.serverFluidManager.getChunk(worldX, worldY, worldZ);
// float[][] macroValues = chunk.getMacroValues();//Globals.serverTerrainManager.getRad5MacroValues(message.getworldX(), message.getworldY());
// long[][] randomizer = chunk.getRandomizer();//Globals.serverTerrainManager.getRandomizer(message.getworldX(), message.getworldY());
//The length along each access of the chunk data. Typically, should be at least 17.
//Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate
//chunk data that connects seamlessly to the next chunk.
int xWidth = chunk.getWeights().length;
int yWidth = chunk.getWeights()[0].length;
int zWidth = chunk.getWeights()[0][0].length;
ByteBuffer buffer = ByteBuffer.allocate(xWidth*yWidth*zWidth*(4+4+4+4));
FloatBuffer floatView = buffer.asFloatBuffer();
for(int x = 0; x < xWidth; x++){
for(int y = 0; y < yWidth; y++){
for(int z = 0; z < zWidth; z++){
floatView.put(chunk.getWeights()[x][y][z]);
}
}
}
for(int x = 0; x < xWidth; x++){
for(int y = 0; y < yWidth; y++){
for(int z = 0; z < zWidth; z++){
floatView.put(chunk.getVelocityX()[x][y][z]);
}
}
}
for(int x = 0; x < xWidth; x++){
for(int y = 0; y < yWidth; y++){
for(int z = 0; z < zWidth; z++){
floatView.put(chunk.getVelocityY()[x][y][z]);
}
}
}
for(int x = 0; x < xWidth; x++){
for(int y = 0; y < yWidth; y++){
for(int z = 0; z < zWidth; z++){
floatView.put(chunk.getVelocityZ()[x][y][z]);
}
}
}
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array()));
}
static void sendWorldMetadata(ServerConnectionHandler connectionHandler){
//world metadata
connectionHandler.addMessagetoOutgoingQueue(

View File

@ -17,6 +17,7 @@ import electrosphere.renderer.model.Material;
import electrosphere.renderer.model.Mesh;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
@ -559,7 +560,7 @@ public class FluidChunkModelGeneration {
return new Vector3f(x,y,z);
}
public static FluidChunkModelData generateFluidChunkData(float[][][] weightGrid, int[][][] typeGrid){
public static FluidChunkModelData generateFluidChunkData(float[][][] weightGrid){
// 5 6
// +-------------+ +-----5-------+ ^ Y
@ -593,6 +594,9 @@ public class FluidChunkModelGeneration {
for(int x = 0; x < weightGrid.length - 1; x++){
for(int y = 0; y < weightGrid[0].length - 1; y++){
for(int z = 0; z < weightGrid[0][0].length - 1; z++){
if(x == 3 && y == 3 && z == 3){
System.out.println("erihjy at 3,3,3 " + weightGrid[x][y][z]);
}
//push the current cell's values into the gridcell
currentCell.setValues(
new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0),
@ -665,6 +669,10 @@ public class FluidChunkModelGeneration {
UVs.add(temp[1]);
}
System.out.println("weight at 3 3 3 " + weightGrid[3][3][3]);
System.out.println("Fluid verts: " + vertsFlat.size());
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs
FluidChunkModelData rVal = new FluidChunkModelData(vertsFlat, normalsFlat, elementsFlat, UVs);
return rVal;
@ -768,7 +776,13 @@ public class FluidChunkModelGeneration {
ex.printStackTrace();
}
float halfChunk = ServerTerrainChunk.CHUNK_DIMENSION / 2.0f;
mesh.updateBoundingSphere(
halfChunk,
halfChunk,
halfChunk,
(float)Math.sqrt(halfChunk * halfChunk + halfChunk * halfChunk + halfChunk * halfChunk)
);
glBindVertexArray(0);
@ -796,6 +810,7 @@ public class FluidChunkModelGeneration {
m.setParent(rVal);
rVal.getMeshes().add(m);
rVal.setBoundingSphere(m.getBoundingSphere());
return rVal;
}

View File

@ -599,6 +599,9 @@ public class TerrainChunkModelGeneration {
for(int x = 0; x < terrainGrid.length - 1; x++){
for(int y = 0; y < terrainGrid[0].length - 1; y++){
for(int z = 0; z < terrainGrid[0][0].length - 1; z++){
if(x == 0 && y == 0 && z == 0){
System.out.println("asdf");
}
//push the current cell's values into the gridcell
currentCell.setValues(
new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0),

View File

@ -21,6 +21,8 @@ import electrosphere.server.content.ServerContentManager;
import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.manager.ServerFluidManager;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -43,6 +45,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Realm parent;
//Manager for terrain for this particular cell manager
ServerTerrainManager serverTerrainManager;
//manager for fluids for this particular cell manager
ServerFluidManager serverFluidManager;
//lock for terrain editing
Semaphore terrainEditLock = new Semaphore(1);
//manager for getting entities to fill in a cell
@ -52,9 +56,15 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* Constructor
* @param parent The gridded data cell manager's parent realm
*/
public GriddedDataCellManager(Realm parent, ServerTerrainManager serverTerrainManager, ServerContentManager serverContentManager) {
public GriddedDataCellManager(
Realm parent,
ServerTerrainManager serverTerrainManager,
ServerFluidManager serverFluidManager,
ServerContentManager serverContentManager
) {
this.parent = parent;
this.serverTerrainManager = serverTerrainManager;
this.serverFluidManager = serverFluidManager;
this.serverContentManager = serverContentManager;
}
@ -344,6 +354,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
for(ServerDataCell cell : loadedCells){
Globals.microSimulation.simulate(cell, parent.getHitboxManager());
}
//simulate fluid
this.serverFluidManager.simulate();
loadedCellsLock.release();
updatePlayerPositions();
}
@ -357,6 +369,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return serverTerrainManager;
}
/**
* Gets the server fluid manager for this realm if it exists
* @return The server fluid manager if it exists, null otherwise
*/
public ServerFluidManager getServerFluidManager(){
return serverFluidManager;
}
/**
* Runs code to generate physics entities and register cell in a dedicated thread.
* Because cell hasn't been registered yet, no simulation is performed until the physics is created.
@ -461,4 +481,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return cellPositionMap.get(cell);
}
@Override
public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition) {
return serverFluidManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z);
}
}

View File

@ -53,7 +53,7 @@ public class RealmManager {
//create realm
Realm realm = new Realm(collisionEngine, new HitboxManager());
//create function classes
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverContentManager);
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverFluidManager,Globals.serverContentManager);
EntityDataCellMapper entityDataCellMapper = new EntityDataCellMapper();
//init gridded manager
griddedDataCellManager.init(serverWorldData);

View File

@ -2,6 +2,7 @@ package electrosphere.server.datacell.interfaces;
import org.joml.Vector3i;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/**
@ -40,5 +41,13 @@ public interface VoxelCellManager {
* @param type The type to set the voxel to
*/
public void editChunk(Vector3i worldPosition, Vector3i voxelPosition, float weight, int type);
/**
* Gets the fluid chunk at a given world position
* @param worldPosition The world position
* @return the fluid chunk
*/
public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition);
}

View File

@ -0,0 +1,217 @@
package electrosphere.server.fluid.diskmap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.DeflaterInputStream;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.server.Server;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.FileUtils;
/**
* An interface for accessing the disk map of chunk information
*/
public class FluidDiskMap {
//The map of world position+chunk type to the file that actually houses that information
Map<String,String> worldPosFileMap = new HashMap<String,String>();
/**
* Constructor
*/
public FluidDiskMap(){
}
/**
* Gets a key for a given chunk file based on a world coordinate
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
private static String getFluidChunkKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ + "f";
}
/**
* Initializes a diskmap based on a given save name
* @param saveName The save name
*/
public void init(String saveName){
LoggerInterface.loggerEngine.DEBUG("INIT CHUNK MAP " + saveName);
if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){
worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "fluid.map", Map.class);
LoggerInterface.loggerEngine.DEBUG("POS FILE MAP: " + worldPosFileMap.keySet());
} else {
worldPosFileMap = new HashMap<String,String>();
}
}
/**
* Saves the disk map to disk
*/
public void save(){
FileUtils.serializeObjectToSavePath(Globals.currentSaveName, "fluid.map", worldPosFileMap);
}
/**
* Checks if the map contains a given chunk position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return True if the map contains the chunk, false otherwise
*/
public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ));
}
/**
* Gets the server fluid chunk from disk if it exists, otherwise returns null
* @param worldX The x coordinate
* @param worldY The y coordinate
* @param worldZ The z coordinate
* @return The server fluid chunk if it exists, null otherwise
*/
public ServerFluidChunk getFluidChunk(int worldX, int worldY, int worldZ){
LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ);
ServerFluidChunk rVal = null;
if(containsFluidAtPosition(worldX, worldY, worldZ)){
//read file
String fileName = worldPosFileMap.get(getFluidChunkKey(worldX, worldY, worldZ));
byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSaveName, fileName);
//decompress
byte[] rawData = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out);
try {
inflaterInputStream.write(rawDataCompressed);
inflaterInputStream.flush();
inflaterInputStream.close();
rawData = out.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//parse
if(rawData != null){
ByteBuffer buffer = ByteBuffer.wrap(rawData);
FloatBuffer floatView = buffer.asFloatBuffer();
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
float[][][] weights = new float[DIM][DIM][DIM];
float[][][] velocityX = new float[DIM][DIM][DIM];
float[][][] velocityY = new float[DIM][DIM][DIM];
float[][][] velocityZ = new float[DIM][DIM][DIM];
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
weights[x][y][z] = floatView.get();
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
velocityX[x][y][z] = floatView.get();
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
velocityY[x][y][z] = floatView.get();
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
velocityZ[x][y][z] = floatView.get();
}
}
}
rVal = new ServerFluidChunk(worldX, worldY, worldZ, weights, velocityX, velocityY, velocityZ);
}
}
return rVal;
}
/**
* Saves a fluid chunk to disk
* @param fluidChunk The fluid chunk
*/
public void saveToDisk(ServerFluidChunk fluidChunk){
LoggerInterface.loggerEngine.DEBUG("Save to disk: " + fluidChunk.getWorldX() + " " + fluidChunk.getWorldY() + " " + fluidChunk.getWorldZ());
//get the file name for this chunk
String fileName = null;
String chunkKey = getFluidChunkKey(fluidChunk.getWorldX(),fluidChunk.getWorldY(),fluidChunk.getWorldZ());
if(worldPosFileMap.containsKey(chunkKey)){
fileName = worldPosFileMap.get(chunkKey);
} else {
fileName = chunkKey + ".dat";
}
//generate binary for the file
float[][][] weights = fluidChunk.getWeights();
float[][][] velocityX = fluidChunk.getVelocityX();
float[][][] velocityY = fluidChunk.getVelocityY();
float[][][] velocityZ = fluidChunk.getVelocityZ();
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
ByteBuffer buffer = ByteBuffer.allocate(DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4);
FloatBuffer floatView = buffer.asFloatBuffer();
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
floatView.put(weights[x][y][z]);
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
floatView.put(velocityX[x][y][z]);
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
floatView.put(velocityY[x][y][z]);
}
}
}
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
floatView.put(velocityZ[x][y][z]);
}
}
}
//compress
ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream deflaterInputStream = new DeflaterOutputStream(out);
try {
deflaterInputStream.write(buffer.array());
deflaterInputStream.flush();
deflaterInputStream.close();
//write to disk
FileUtils.saveBinaryToSavePath(Globals.currentSaveName, fileName, out.toByteArray());
//save to the map of filenames
worldPosFileMap.put(chunkKey,fileName);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,35 @@
package electrosphere.server.fluid.generation;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.models.FluidModel;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
public class ArenaFluidGenerator implements FluidGenerator {
@Override
public ServerFluidChunk generateChunk(int worldX, int worldY, int worldZ) {
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
float[][][] velocityX = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
float[][][] velocityY = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
float[][][] velocityZ = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
ServerFluidChunk chunk = new ServerFluidChunk(worldX, worldY, worldZ, weights, velocityX, velocityY, velocityZ);
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
weights[x][y][z] = -1.0f;
}
}
}
weights[3][3][3] = 0.8f;
return chunk;
}
@Override
public void setModel(FluidModel model) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setModel'");
}
}

View File

@ -0,0 +1,26 @@
package electrosphere.server.fluid.generation;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.models.FluidModel;
/**
* Generates fluid
*/
public interface FluidGenerator {
/**
* Generates a chunk given an x, y, and z
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The chunk
*/
public ServerFluidChunk generateChunk(int worldX, int worldY, int worldZ);
/**
* Sets the fluid model for the generation algorithm
* @param model The fluid model
*/
public void setModel(FluidModel model);
}

View File

@ -0,0 +1,129 @@
package electrosphere.server.fluid.manager;
import org.joml.Vector3f;
import org.joml.Vector3i;
/**
* Is a single chunk of terrain on the server
*/
public class ServerFluidChunk {
int worldX, worldY, worldZ;
float[][][] weights;
float[][][] velocityX;
float[][][] velocityY;
float[][][] velocityZ;
public ServerFluidChunk(
int worldX,
int worldY,
int worldZ,
float[][][] weights,
float[][][] velocityX,
float[][][] velocityY,
float[][][] velocityZ
) {
this.worldX = worldX;
this.worldY = worldY;
this.worldZ = worldZ;
this.weights = weights;
this.velocityX = velocityX;
this.velocityY = velocityY;
this.velocityZ = velocityZ;
}
public int getWorldX() {
return worldX;
}
public int getWorldY() {
return worldY;
}
public int getWorldZ() {
return worldZ;
}
/**
* Gets the world position of this terrain chunk as a joml Vector
* @return The vector
*/
public Vector3i getWorldPosition(){
return new Vector3i(worldX,worldY,worldZ);
}
public float[][][] getWeights() {
return weights;
}
/**
* Gets the weight of a voxel at a poisiton
* @param localPosition The local position
* @return The weight of the specified voxel
*/
public float getWeight(Vector3i localPosition){
return getWeight(localPosition.x,localPosition.y,localPosition.z);
}
/**
* Gets the weight of a voxel at a poisiton
* @param x The x coordinate
* @param y The y coordinate
* @param z The z coordinate
* @return The weight of the specified voxel
*/
public float getWeight(int x, int y, int z){
return weights[x][y][z];
}
//get velocity x
public float[][][] getVelocityX() {
return velocityX;
}
//set velocity x
public void setVelocityX(float[][][] velocityX) {
this.velocityX = velocityX;
}
//get velocity y
public float[][][] getVelocityY() {
return velocityY;
}
//set velocity y
public void setVelocityY(float[][][] velocityY) {
this.velocityY = velocityY;
}
//get velocity z
public float[][][] getVelocityZ() {
return velocityZ;
}
//set velocity z
public void setVelocityZ(float[][][] velocityZ) {
this.velocityZ = velocityZ;
}
//get a velocity at a given x, y and z as a Vector3f
public Vector3f getVelocity(int x, int y, int z){
return new Vector3f(velocityX[x][y][z],velocityY[x][y][z],velocityZ[x][y][z]);
}
//set a velocity at a given x, y, and z given three ints
public void setVelocity(int x, int y, int z, float velX, float velY, float velZ){
velocityX[x][y][z] = velX;
velocityY[x][y][z] = velY;
velocityZ[x][y][z] = velZ;
}
}

View File

@ -0,0 +1,284 @@
package electrosphere.server.fluid.manager;
import electrosphere.engine.Globals;
import electrosphere.server.fluid.diskmap.FluidDiskMap;
import electrosphere.server.fluid.generation.ArenaFluidGenerator;
import electrosphere.server.fluid.generation.FluidGenerator;
import electrosphere.server.fluid.models.FluidModel;
import electrosphere.util.FileUtils;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Vector3i;
/**
* Provides an interface for the server to query information about fluid
*/
public class ServerFluidManager {
//The size of the world in discrete units * must be multiple of 200
int worldSizeDiscrete;
//The vertical multiplier applied to the statically generated fluid
int verticalInterpolationRatio;
float interpolationRandomDampener;
long seed;
//The model of the fluid this manager is managing
FluidModel model;
//In memory cache of chunk data
//Basic idea is we associate string that contains chunk x&y&z with elevation
//While we incur a penalty with converting ints -> string, think this will
//offset regenerating the array every time we want a new one
int cacheSize = 500;
Map<String, ServerFluidChunk> chunkCache;
List<String> chunkCacheContents;
//The map of chunk position <-> file on disk containing chunk data
FluidDiskMap chunkDiskMap = null;
//The generation algorithm for this fluid manager
FluidGenerator chunkGenerator;
/**
* Constructor
*/
public ServerFluidManager(
int worldSizeDiscrete,
int verticalInterpolationRatio,
float interpolationRandomDampener,
long seed,
FluidGenerator chunkGenerator
){
this.worldSizeDiscrete = worldSizeDiscrete;
this.verticalInterpolationRatio = verticalInterpolationRatio;
this.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
this.interpolationRandomDampener = interpolationRandomDampener;
this.seed = seed;
this.chunkGenerator = chunkGenerator;
}
ServerFluidManager(){
}
/**
* Constructs an arena fluid manager
* @return The arena fluid manager
*/
public static ServerFluidManager constructArenaFluidManager(){
ServerFluidManager rVal = new ServerFluidManager();
rVal.worldSizeDiscrete = 2;
rVal.verticalInterpolationRatio = 0;
rVal.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
rVal.interpolationRandomDampener = 0.0f;
rVal.chunkGenerator = new ArenaFluidGenerator();
return rVal;
}
/**
* Generates a fluid model for the manager
*/
public void generate(){
// FluidGenerator terrainGen = new FluidGenerator();
// terrainGen.setInterpolationRatio(worldSizeDiscrete/200);
// terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio);
// terrainGen.setRandomSeed(seed);
// model = terrainGen.generateModel();
// this.chunkGenerator.setModel(model);
// model.setInterpolationRandomDampener(interpolationRandomDampener);
// this.chunkDiskMap = new ChunkDiskMap();
}
/**
* Saves the fluid model backing this manager to a save file
* @param saveName The name of the save
*/
public void save(String saveName){
ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4);
FloatBuffer floatView = buffer.asFloatBuffer();
for(int x = 0; x < model.getElevation().length; x++){
floatView.put(model.getElevation()[x]);
}
floatView.flip();
FileUtils.saveBinaryToSavePath(saveName, "./fluid.dat", buffer.array());
FileUtils.serializeObjectToSavePath(saveName, "./fluid.json", model);
//for each chunk, save via disk map
for(String chunkKey : chunkCacheContents){
ServerFluidChunk chunk = chunkCache.get(chunkKey);
chunkDiskMap.saveToDisk(chunk);
}
//save disk map itself
if(chunkDiskMap != null){
chunkDiskMap.save();
}
}
/**
* Loads a fluid manager from a save file
* @param saveName The name of the save
*/
public void load(String saveName){
//load fluid model
model = FileUtils.loadObjectFromSavePath(saveName, "./fluid.json", FluidModel.class);
chunkGenerator.setModel(model);
byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./fluid.dat");
ByteBuffer buffer = ByteBuffer.wrap(data);
FloatBuffer floatView = buffer.asFloatBuffer();
float[][] elevation = new float[Globals.serverWorldData.getWorldSizeDiscrete()][Globals.serverWorldData.getWorldSizeDiscrete()];
for(int x = 0; x < Globals.serverWorldData.getWorldSizeDiscrete(); x++){
for(int y = 0; y < Globals.serverWorldData.getWorldSizeDiscrete(); y++){
elevation[x][y] = floatView.get();
}
}
model.setElevationArray(elevation);
//load chunk disk map
chunkDiskMap = new FluidDiskMap();
chunkDiskMap.init(saveName);
}
public float[][] getFluidAtChunk(int x, int y){
return model.getElevationForChunk(x, y);
}
public double getHeightAtPosition(double x, double y, double z){
return y;
}
public int getWorldDiscreteSize(){
return worldSizeDiscrete;
}
public float getDiscreteValue(int x, int y){
if(model != null){
return model.getElevation()[x][y];
} else {
return 0;
}
}
public int getDynamicInterpolationRatio(){
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
if(model != null){
return model.getDynamicInterpolationRatio();
} else {
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING
return 0;
}
}
public float getRandomDampener(){
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
if(model != null){
return model.getRandomDampener();
} else {
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING
return 0.0f;
}
}
/**
* Gets the fluid model backing this fluid manager
* @return The fluid model
*/
public FluidModel getModel() {
return model;
}
/**
* Gets the key for a given world position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
public String getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ;
}
/**
* Gets a server fluid chunk
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
* @return The ServerFluidChunk
*/
public ServerFluidChunk getChunk(int worldX, int worldY, int worldZ){
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
String key = getKey(worldX,worldY,worldZ);
ServerFluidChunk returnedChunk = null;
if(chunkCache.containsKey(key)){
chunkCacheContents.remove(key);
chunkCacheContents.add(0, key);
returnedChunk = chunkCache.get(key);
return returnedChunk;
} else {
if(chunkCacheContents.size() > cacheSize){
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
chunkCache.remove(oldChunk);
}
//pull from disk if it exists
if(chunkDiskMap != null){
if(chunkDiskMap.containsFluidAtPosition(worldX, worldY, worldZ)){
returnedChunk = chunkDiskMap.getFluidChunk(worldX, worldY, worldZ);
}
}
//generate if it does not exist
if(returnedChunk == null){
returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
}
chunkCache.put(key, returnedChunk);
chunkCacheContents.add(key);
return returnedChunk;
}
}
/**
* Saves a given position's chunk to disk.
* Uses the current global save name
* @param position The position to save
*/
public void savePositionToDisk(Vector3i position){
chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z));
}
/**
* Applies a deform to fluid at a given location
* @param worldPos The world coordinates of the chunk to modify
* @param voxelPos The voxel coordinates of the voxel to modify
* @param weight The weight to set it to
* @param value The value to set it to
*/
public void deformFluidAtLocationToValue(Vector3i worldPos, Vector3i voxelPos, float weight, int value){
// TerrainModification modification = new TerrainModification(worldPos,voxelPos,weight,value);
// //could be null if, for instance, arena mode
// if(model != null){
// model.addModification(modification);
// }
// String key = getKey(worldPos.x,worldPos.y,worldPos.z);
// if(chunkCache.containsKey(key)){
// ServerFluidChunk chunk = chunkCache.get(key);
// chunk.addModification(modification);
// }
}
/**
* Simulates all active fluid chunks
*/
public void simulate(){
//TODO: implement
}
}

View File

@ -0,0 +1,343 @@
package electrosphere.server.fluid.models;
import electrosphere.util.annotation.Exclude;
public class FluidModel {
int dynamicInterpolationRatio;
float interpolationRandomDampener = 0.4f;
int discreteArrayDimension;
@Exclude
private float[][] elevation;
float realMountainThreshold;
float realOceanThreshold;
FluidModel() {
}
public FluidModel(
int dimension,
int dynamicInterpolationRatio
){
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
this.discreteArrayDimension = dimension;
}
public static FluidModel constructFluidModel(int dimension, int dynamicInterpolationRatio){
FluidModel rVal = new FluidModel();
rVal.discreteArrayDimension = dimension;
rVal.dynamicInterpolationRatio = dynamicInterpolationRatio;
return rVal;
}
public float[][] getElevation(){
return elevation;
}
public void setInterpolationRandomDampener(float f){
interpolationRandomDampener = f;
}
/**
* Dynamically interpolates a chunk of a specific size from the pre-existing elevation map
* @param x The x position on the elevation map to get a chunk from
* @param y The y position on the elevation map to get a chunk from
* @return Dynamically interpolated float array of elevations of chunk
*/
public float[][] getElevationForChunk(int x, int y){
//this is what we intend to return from the function
float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio];
/*
So we're looking at chunk x,y
if this is our grid:
4 0.1 0.2 0.3 0.4
^
| 3 0.1 0.2 0.3 0.4
|
| 2 0.1 0.2 0.3 0.4
x 1 0.1 0.2 0.3 0.4
0 1 2 3
y ---- >
say we're looking at x=2,y=1
"macroValues" should contain the values for bounds x = [1,3] and y = [0,2]
the goal is to have the "center" of the output chunk have the value the
elevation grid at x=2,y=1
*/
//set macroValues
float[][] macroValues = getMacroValuesAtPosition(x,y);
int halfLength = dynamicInterpolationRatio/2;
/*
Four quadrants we're generating
_____________________
|1 |2 |
| | |
| | |
| | |
|__________|__________|
|3 |4 |
| | |
| | |
|__________|__________|
First set of loops is quadrant 1
then quadrant 2
then quadrant 3
then quadrant 4
*/
int outXOffset = 0;
int outYOffset = 0;
for(int i = 0; i < halfLength; i++){
for(int j = 0; j < halfLength; j++){
rVal[i+outXOffset][j+outYOffset] =
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] +
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] +
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][1] +
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1]
;
}
}
outXOffset = halfLength;
for(int i = 0; i < halfLength; i++){
for(int j = 0; j < halfLength; j++){
rVal[i+outXOffset][j+outYOffset] =
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] +
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] +
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] +
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][1]
;
}
}
outXOffset = 0;
outYOffset = halfLength;
for(int i = 0; i < halfLength; i++){
for(int j = 0; j < halfLength; j++){
rVal[i+outXOffset][j+outYOffset] =
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] +
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] +
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][2] +
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2]
;
}
}
outXOffset = halfLength;
for(int i = 0; i < halfLength; i++){
for(int j = 0; j < halfLength; j++){
rVal[i+outXOffset][j+outYOffset] =
(1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] +
(1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] +
(1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] +
(1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][2]
;
}
}
return rVal;
}
/*
So we're looking at chunk x,y
if this is our grid:
4 0.1 0.2 0.3 0.4
^
| 3 0.1 0.2 0.3 0.4
|
| 2 0.1 0.2 0.3 0.4
x 1 0.1 0.2 0.3 0.4
0 1 2 3
y ---- >
say we're looking at x=2,y=1
"macroValues" should contain the values for x = [1,3] and y = [0,2]
the goal is to have the "center" of the output chunk have the value the
elevation grid at x=2,y=1
*/
public float[][] getMacroValuesAtPosition(int x, int y){
float[][] rVal = new float[3][3];
rVal[1][1] = elevation[x][y];
if(x - 1 >= 0){
rVal[0][1] = elevation[x-1][y];
if(y - 1 >= 0){
rVal[0][0] = elevation[x-1][y-1];
} else {
rVal[0][0] = 0;
}
if(y + 1 < discreteArrayDimension){
rVal[0][2] = elevation[x-1][y+1];
} else {
rVal[0][2] = 0;
}
} else {
rVal[0][0] = 0;
rVal[0][1] = 0;
rVal[0][2] = 0;
}
if(x + 1 < discreteArrayDimension){
rVal[2][1] = elevation[x+1][y];
if(y - 1 >= 0){
rVal[2][0] = elevation[x+1][y-1];
} else {
rVal[2][0] = 0;
}
if(y + 1 < discreteArrayDimension){
rVal[2][2] = elevation[x+1][y+1];
} else {
rVal[2][2] = 0;
}
} else {
rVal[2][0] = 0;
rVal[2][1] = 0;
rVal[2][2] = 0;
}
if(y - 1 >= 0){
rVal[1][0] = elevation[x][y-1];
} else {
rVal[1][0] = 0;
}
if(y + 1 < discreteArrayDimension){
rVal[1][2] = elevation[x][y+1];
} else {
rVal[1][2] = 0;
}
return rVal;
}
public float[][] getRad5MacroValuesAtPosition(int x, int y){
float[][] rVal = new float[5][5];
for(int i = -2; i < 3; i++){
for(int j = -2; j < 3; j++){
if(x + i >= 0 && x + i < discreteArrayDimension && y + j >= 0 && y + j < discreteArrayDimension){
rVal[i+2][j+2] = elevation[x+i][y+j];
} else {
rVal[i+2][j+2] = 0;
}
}
}
return rVal;
}
/*
So we're looking at chunk x,y
if this is our grid:
4 0.1 0.2 0.3 0.4
^
| 3 0.1 0.2 0.3 0.4
|
| 2 0.1 0.2 0.3 0.4
x 1 0.1 0.2 0.3 0.4
0 1 2 3
y ---- >
say we're looking at x=2,y=1
"macroValues" should contain the values for x = [1,3] and y = [0,2]
the goal is to have the "center" of the output chunk have the value the
elevation grid at x=2,y=1
*/
public float getRandomDampener(){
return interpolationRandomDampener;
}
public int getDynamicInterpolationRatio(){
return dynamicInterpolationRatio;
}
public float getRealMountainThreshold() {
return realMountainThreshold;
}
public float getRealOceanThreshold() {
return realOceanThreshold;
}
public String getModificationKey(int x, int y, int z){
return x + "_" + y + "_" + z;
}
// public void addModification(TerrainModification modification){
// String key = getModificationKey(modification.getWorldPos().x,modification.getWorldPos().y,modification.getWorldPos().z);
// ModificationList list;
// if(!modifications.containsKey(key)){
// list = new ModificationList();
// modifications.put(key, list);
// } else {
// list = modifications.get(key);
// }
// list.addModification(modification);
// }
// public boolean containsModificationsAtCoord(int worldX, int worldY, int worldZ){
// return modifications.containsKey(getModificationKey(worldX, worldY, worldZ));
// }
// public ModificationList getModifications(int worldX, int worldY, int worldZ){
// // System.out.println("Got modifications at " + worldX + " " + worldY);
// return modifications.get(getModificationKey(worldX, worldY, worldZ));
// }
/**
* Sets the elevation array (For instance when read from save file on loading a save)
* @param elevation The elevation array to set to
*/
public void setElevationArray(float[][] elevation){
this.elevation = elevation;
}
}