fluids are rendering albeit poorly
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				studiorailgun/Renderer/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	studiorailgun/Renderer/pipeline/head This commit looks good
				
			This commit is contained in:
		
							parent
							
								
									f328d16bab
								
							
						
					
					
						commit
						97edfacd41
					
				
							
								
								
									
										208
									
								
								assets/Shaders/fluid2/fluid2.fs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								assets/Shaders/fluid2/fluid2.fs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,208 @@ | ||||
| #version 330 core | ||||
| 
 | ||||
| #define NR_POINT_LIGHTS 10 | ||||
| 
 | ||||
| out vec4 FragColor; | ||||
| 
 | ||||
| 
 | ||||
| layout (std140) uniform Lights { | ||||
|     //                                        this is how many      because we have to align | ||||
|     //                                        bytes it SHOULD       in multiples of 16, this | ||||
|     //                                        take                  it where it ACTUALLY is | ||||
|     // | ||||
|     //refer: https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL | ||||
|     // | ||||
|     //                                        base alignment        aligned offset | ||||
|     //direct light | ||||
|     vec3 dLDirection;                      // 16                    0 | ||||
|     vec3 dLAmbient;                        // 16                    16 | ||||
|     vec3 dLDiffuse;                        // 16                    32 | ||||
|     vec3 dLSpecular;                       // 16                    48 | ||||
| 
 | ||||
|     //point light | ||||
|     vec3 pLposition[NR_POINT_LIGHTS];      // 16*10                 64 | ||||
|     float pLconstant[NR_POINT_LIGHTS];     // 16*10                 224 | ||||
|     float pLlinear[NR_POINT_LIGHTS];       // 16*10                 384 | ||||
|     float pLquadratic[NR_POINT_LIGHTS];    // 16*10                 544 | ||||
|     vec3 pLambient[NR_POINT_LIGHTS];       // 16*10                 704 | ||||
|     vec3 pLdiffuse[NR_POINT_LIGHTS];       // 16*10                 864 | ||||
|     vec3 pLspecular[NR_POINT_LIGHTS];      // 16*10                 1024 | ||||
| 
 | ||||
|     //for a total size of   1184 | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| struct Material { | ||||
|     sampler2D diffuse; | ||||
|     sampler2D specular; | ||||
|     float shininess; | ||||
| };  | ||||
| 
 | ||||
| in vec3 FragPos; | ||||
| in vec3 Normal; | ||||
| in vec2 texPlane1; | ||||
| in vec2 texPlane2; | ||||
| in vec2 texPlane3; | ||||
| in vec4 FragPosLightSpace; | ||||
| 
 | ||||
| 
 | ||||
| uniform vec3 viewPos; | ||||
| // uniform DirLight dirLight; | ||||
| // uniform PointLight pointLights[NR_POINT_LIGHTS]; | ||||
| // uniform SpotLight spotLight; | ||||
| uniform Material material; | ||||
| 
 | ||||
| //texture stuff | ||||
| // uniform sampler2D ourTexture; | ||||
| uniform int hasTransparency; | ||||
| // uniform sampler2D specularTexture; | ||||
| 
 | ||||
| //light depth map | ||||
| uniform sampler2D shadowMap; | ||||
| 
 | ||||
| 
 | ||||
| // function prototypes | ||||
| // vec3 CalcDirLight(vec3 normal, vec3 viewDir); | ||||
| // vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir); | ||||
| // vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir); | ||||
| float calcLightIntensityTotal(vec3 normal); | ||||
| float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal); | ||||
| vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, Material material); | ||||
| 
 | ||||
| void main(){ | ||||
|     vec3 norm = normalize(Normal); | ||||
|     vec3 viewDir = normalize(viewPos - FragPos); | ||||
|      | ||||
|     //grab light intensity | ||||
|     float lightIntensity = calcLightIntensityTotal(norm); | ||||
| 
 | ||||
|     //get color of base texture | ||||
|     vec3 textureColor = 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/fluid2/fluid2.vs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								assets/Shaders/fluid2/fluid2.vs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| //Vertex Shader | ||||
| #version 330 core | ||||
| 
 | ||||
| //defines | ||||
| #define TEXTURE_MAP_SCALE 3.0 | ||||
| 
 | ||||
| 
 | ||||
| //input buffers | ||||
| layout (location = 0) in vec3 aPos; | ||||
| layout (location = 1) in vec3 aNormal; | ||||
| layout (location = 4) in vec2 aTex; | ||||
| 
 | ||||
| 
 | ||||
| //coordinate space transformation matrices | ||||
| uniform mat4 transform; | ||||
| uniform mat4 model; | ||||
| uniform mat4 view; | ||||
| uniform mat4 projection; | ||||
| uniform mat4 lightSpaceMatrix; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //output buffers | ||||
| out vec3 Normal; | ||||
| out vec3 FragPos; | ||||
| out vec2 texPlane1; | ||||
| out vec2 texPlane2; | ||||
| out vec2 texPlane3; | ||||
| out vec4 FragPosLightSpace; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void main() { | ||||
|     //normalize posiiton and normal | ||||
|     vec4 FinalVertex = vec4(aPos, 1.0); | ||||
|     vec4 FinalNormal = vec4(aNormal, 1.0); | ||||
| 
 | ||||
| 
 | ||||
|     //push frag, normal, and texture positions to fragment shader | ||||
|     FragPos = vec3(model * FinalVertex); | ||||
|     Normal = mat3(transpose(inverse(model))) * aNormal; | ||||
|      | ||||
|     //reference https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/ | ||||
|     texPlane1 = aPos.zy * TEXTURE_MAP_SCALE; | ||||
|     texPlane2 = aPos.xz * TEXTURE_MAP_SCALE; | ||||
|     texPlane3 = aPos.xy * TEXTURE_MAP_SCALE; | ||||
| 
 | ||||
|     //flip first coordinate if the normal is negative | ||||
|     //this minimizes texture flipping | ||||
|     texPlane1.x = texPlane1.x * sign(Normal.x); | ||||
|     texPlane2.x = texPlane2.x * sign(Normal.y); | ||||
|     texPlane3.x = texPlane3.x * sign(Normal.z); | ||||
| 
 | ||||
| 
 | ||||
|     //shadow map stuff | ||||
|     FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0); | ||||
| 
 | ||||
|      | ||||
|     //set final position with opengl space | ||||
|     gl_Position = projection * view * model * FinalVertex; | ||||
| } | ||||
| @ -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 | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -184,6 +184,23 @@ | ||||
|                         "worldZ", | ||||
|                         "chunkData" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "messageName" : "RequestFluidData", | ||||
|                     "data" : [ | ||||
|                         "worldX", | ||||
|                         "worldY", | ||||
|                         "worldZ" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "messageName" : "sendFluidData", | ||||
|                     "data" : [ | ||||
|                         "worldX", | ||||
|                         "worldY", | ||||
|                         "worldZ", | ||||
|                         "chunkData" | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|  | ||||
| @ -11,6 +11,7 @@ import electrosphere.entity.EntityUtils; | ||||
| import electrosphere.entity.Scene; | ||||
| import electrosphere.entity.types.attach.AttachUtils; | ||||
| 
 | ||||
| @Deprecated | ||||
| public class ClientEntityCullingManager { | ||||
| 
 | ||||
|     Scene scene; | ||||
| @ -40,38 +41,4 @@ public class ClientEntityCullingManager { | ||||
|         target.putData(EntityDataStrings.DATA_STRING_DRAW, true); | ||||
|     } | ||||
| 
 | ||||
|     public void clearOutOfBoundsEntities(){ | ||||
|         if(Globals.commonWorldData != null && Globals.playerEntity != null && Globals.clientPlayerData != null){ | ||||
|             Vector3d playerCharacterPos = EntityUtils.getPosition(Globals.playerEntity); | ||||
|             int playerCharacterWorldX = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.x); | ||||
|             int playerCharacterWorldY = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.y); | ||||
|             int playerCharacterWorldZ = Globals.clientWorldData.convertRealToWorld(playerCharacterPos.z); | ||||
|             if( | ||||
|                 playerCharacterWorldX != Globals.clientPlayerData.getWorldPos().x||  | ||||
|                 playerCharacterWorldY != Globals.clientPlayerData.getWorldPos().y || | ||||
|                 playerCharacterWorldZ != Globals.clientPlayerData.getWorldPos().z | ||||
|             ){ | ||||
|                 for(Entity entity : scene.getEntityList()){ | ||||
|                     if(entity.containsKey(EntityDataStrings.TERRAIN_IS_TERRAIN) || entity.containsKey(EntityDataStrings.ATTACH_PARENT) || entity.containsKey(EntityDataStrings.COLLISION_ENTITY_PARENT)){ | ||||
| 
 | ||||
|                     } else { | ||||
|                         Vector3d position = EntityUtils.getPosition(entity); | ||||
|                         //common world data is initialized with the collision data | ||||
|                         //if this is null then the engine hasn't fully started up yet | ||||
|                         if(position != null){ | ||||
|                             int worldX = Globals.clientWorldData.convertRealToWorld(position.x); | ||||
|                             int worldZ = Globals.clientWorldData.convertRealToWorld(position.z); | ||||
|                             if(!Globals.drawCellManager.coordsInPhysicsSpace(worldX, worldZ)){ | ||||
|                                 //we need to just hide the entity | ||||
|                                 recursiveHide(entity); | ||||
|                             } else { | ||||
|                                 //if the entity is within range and it's not set to visible, make it visible | ||||
|                                 recursiveShow(entity); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -17,48 +17,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]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
							
								
								
									
										240
									
								
								src/main/java/electrosphere/client/fluid/cells/FluidCell.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								src/main/java/electrosphere/client/fluid/cells/FluidCell.java
									
									
									
									
									
										Normal 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; | ||||
|         } | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -19,7 +19,7 @@ import electrosphere.renderer.shader.ShaderProgram; | ||||
|  * | ||||
|  * @author satellite | ||||
|  */ | ||||
| public class FluidDrawCellManager { | ||||
| public class FluidCellManager { | ||||
|      | ||||
|      | ||||
|     //the center of this cell manager's array in cell space | ||||
| @ -35,8 +35,8 @@ public class FluidDrawCellManager { | ||||
|     int miniCellWidth; | ||||
|      | ||||
|     //all currently displaying mini cells | ||||
|     Set<FluidDrawCell> cells; | ||||
|     Map<String,FluidDrawCell> keyCellMap = new HashMap<String,FluidDrawCell>(); | ||||
|     Set<FluidCell> cells; | ||||
|     Map<String,FluidCell> keyCellMap = new HashMap<String,FluidCell>(); | ||||
|     Set<String> hasNotRequested; | ||||
|     Set<String> hasRequested; | ||||
|     Set<String> drawable; | ||||
| @ -59,8 +59,6 @@ public class FluidDrawCellManager { | ||||
|     int worldBoundDiscreteMin = 0; | ||||
|     int worldBoundDiscreteMax = 0; | ||||
|          | ||||
|     //client terrain manager | ||||
|     // ClientTerrainManager clientTerrainManager; | ||||
|      | ||||
|      | ||||
|     //ready to start updating? | ||||
| @ -78,9 +76,9 @@ public class FluidDrawCellManager { | ||||
|      * @param discreteX The initial discrete position X coordinate | ||||
|      * @param discreteY The initial discrete position Y coordinate | ||||
|      */ | ||||
|     public FluidDrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){ | ||||
|     public FluidCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){ | ||||
|         worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f); | ||||
|         cells = new HashSet<FluidDrawCell>(); | ||||
|         cells = new HashSet<FluidCell>(); | ||||
|         hasNotRequested = new HashSet<String>(); | ||||
|         drawable = new HashSet<String>(); | ||||
|         undrawable = new HashSet<String>(); | ||||
| @ -102,7 +100,7 @@ public class FluidDrawCellManager { | ||||
|         update = true; | ||||
|     } | ||||
|      | ||||
|     FluidDrawCellManager(){ | ||||
|     FluidCellManager(){ | ||||
|          | ||||
|     } | ||||
| 
 | ||||
| @ -132,7 +130,7 @@ public class FluidDrawCellManager { | ||||
|                 ){ | ||||
|                 // if(!hasRequested.contains(targetKey)){ | ||||
|                     //client should request chunk data from server | ||||
|                     Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage( | ||||
|                     Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestFluidDataMessage( | ||||
|                         worldPos.x, | ||||
|                         worldPos.y, | ||||
|                         worldPos.z | ||||
| @ -161,7 +159,7 @@ public class FluidDrawCellManager { | ||||
|                 worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() | ||||
|             ){ | ||||
|                 if(containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){ | ||||
|                     FluidDrawCell cell = FluidDrawCell.generateFluidCell( | ||||
|                     FluidCell cell = FluidCell.generateFluidCell( | ||||
|                         worldPos, | ||||
|                         Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z), | ||||
|                         program | ||||
| @ -256,14 +254,14 @@ public class FluidDrawCellManager { | ||||
|      * Clears all cells outside of draw radius | ||||
|      */ | ||||
|     private void clearOutOfBoundsCells(){ | ||||
|         Set<FluidDrawCell> cellsToRemove = new HashSet<FluidDrawCell>(); | ||||
|         for(FluidDrawCell cell : cells){ | ||||
|         Set<FluidCell> cellsToRemove = new HashSet<FluidCell>(); | ||||
|         for(FluidCell cell : cells){ | ||||
|             Vector3d realPos = cell.getRealPos(); | ||||
|             if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){ | ||||
|                 cellsToRemove.add(cell); | ||||
|             } | ||||
|         } | ||||
|         for(FluidDrawCell cell : cellsToRemove){ | ||||
|         for(FluidCell cell : cellsToRemove){ | ||||
|             cells.remove(cell); | ||||
|             String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z); | ||||
|             hasNotRequested.remove(key); | ||||
| @ -358,8 +356,8 @@ public class FluidDrawCellManager { | ||||
|     } | ||||
| 
 | ||||
|     boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ | ||||
|         if(Globals.clientTerrainManager != null){ | ||||
|             return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); | ||||
|         if(Globals.clientFluidManager != null){ | ||||
|             return Globals.clientFluidManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| @ -1,103 +0,0 @@ | ||||
| package electrosphere.client.fluid.cells; | ||||
| 
 | ||||
| import org.joml.Vector3d; | ||||
| import org.joml.Vector3i; | ||||
| import org.ode4j.ode.DBody; | ||||
| 
 | ||||
| import electrosphere.client.fluid.cache.FluidChunkData; | ||||
| import electrosphere.collision.CollisionEngine; | ||||
| import electrosphere.engine.Globals; | ||||
| import electrosphere.entity.ClientEntityUtils; | ||||
| import electrosphere.entity.Entity; | ||||
| import electrosphere.entity.EntityUtils; | ||||
| import electrosphere.entity.types.fluid.FluidChunk; | ||||
| import electrosphere.renderer.shader.ShaderProgram; | ||||
| import electrosphere.renderer.texture.Texture; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @author satellite | ||||
|  */ | ||||
| public class FluidDrawCell { | ||||
|     //the position of the draw cell in world coordinates | ||||
|     Vector3i worldPos; | ||||
|      | ||||
|     FluidChunkData data; | ||||
|      | ||||
|     Entity modelEntity; | ||||
|      | ||||
|     ShaderProgram program; | ||||
|      | ||||
|     DBody physicsObject; | ||||
|      | ||||
|     static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png"); | ||||
|     static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); | ||||
|     static Texture groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); | ||||
|     static Texture groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); | ||||
|      | ||||
|     static { | ||||
| //        groundTextureOne = new Texture("/Textures/Ground/GrassTileable.png"); | ||||
| //        groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); | ||||
| //        groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); | ||||
| //        groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); | ||||
|     } | ||||
|      | ||||
|      | ||||
|     FluidDrawCell(){ | ||||
|          | ||||
|     } | ||||
|      | ||||
|      | ||||
|     /** | ||||
|      * Constructs a drawcell object | ||||
|      */ | ||||
|     public static FluidDrawCell generateFluidCell( | ||||
|             Vector3i worldPos, | ||||
|             FluidChunkData data, | ||||
|             ShaderProgram program | ||||
|     ){ | ||||
|         FluidDrawCell rVal = new FluidDrawCell(); | ||||
|         rVal.worldPos = worldPos; | ||||
|         rVal.program = program; | ||||
|         rVal.data = data; | ||||
|         return rVal; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Generates a drawable entity based on this chunk | ||||
|      */ | ||||
|     public void generateDrawableEntity(){ | ||||
|         if(modelEntity != null){ | ||||
|             Globals.clientScene.deregisterEntity(modelEntity); | ||||
|         } | ||||
|         modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight(), data.getVoxelType()); | ||||
| 
 | ||||
|         ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos()); | ||||
|     } | ||||
| 
 | ||||
|     protected Vector3d getRealPos(){ | ||||
|         return new Vector3d( | ||||
|             worldPos.x * FluidChunkData.CHUNK_SIZE, | ||||
|             worldPos.y * FluidChunkData.CHUNK_SIZE, | ||||
|             worldPos.z * FluidChunkData.CHUNK_SIZE | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Destroys a drawcell including its physics | ||||
|      */ | ||||
|     public void destroy(){ | ||||
|         CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); | ||||
|         collisionEngine.destroyEntityThatHasPhysics(modelEntity); | ||||
|         EntityUtils.cleanUpEntity(modelEntity); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the current chunk data for this draw cell | ||||
|      * @return The chunk data | ||||
|      */ | ||||
|     public FluidChunkData getData(){ | ||||
|         return data; | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -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); | ||||
|     } | ||||
|      | ||||
|  | ||||
| @ -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){ | ||||
|  | ||||
| @ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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"); | ||||
|  | ||||
| @ -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(){ | ||||
|  | ||||
| @ -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 | ||||
|      */ | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
|  | ||||
| @ -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 | ||||
|     */ | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
|  | ||||
| @ -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), | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -53,7 +53,7 @@ public class RealmManager { | ||||
|         //create realm | ||||
|         Realm realm = new Realm(collisionEngine, new HitboxManager()); | ||||
|         //create function classes | ||||
|         GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverContentManager); | ||||
|         GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm,Globals.serverTerrainManager,Globals.serverFluidManager,Globals.serverContentManager); | ||||
|         EntityDataCellMapper entityDataCellMapper = new EntityDataCellMapper(); | ||||
|         //init gridded manager | ||||
|         griddedDataCellManager.init(serverWorldData); | ||||
|  | ||||
| @ -2,6 +2,7 @@ package electrosphere.server.datacell.interfaces; | ||||
| 
 | ||||
| import org.joml.Vector3i; | ||||
| 
 | ||||
| import electrosphere.server.fluid.manager.ServerFluidChunk; | ||||
| import electrosphere.server.terrain.manager.ServerTerrainChunk; | ||||
| 
 | ||||
| /** | ||||
| @ -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); | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,217 @@ | ||||
| package electrosphere.server.fluid.diskmap; | ||||
| 
 | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.FloatBuffer; | ||||
| import java.nio.IntBuffer; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.zip.DeflaterInputStream; | ||||
| import java.util.zip.DeflaterOutputStream; | ||||
| import java.util.zip.InflaterOutputStream; | ||||
| 
 | ||||
| import electrosphere.engine.Globals; | ||||
| import electrosphere.logger.LoggerInterface; | ||||
| import electrosphere.net.server.Server; | ||||
| import electrosphere.server.fluid.manager.ServerFluidChunk; | ||||
| import electrosphere.server.terrain.manager.ServerTerrainChunk; | ||||
| import electrosphere.util.FileUtils; | ||||
| 
 | ||||
| /** | ||||
|  * An interface for accessing the disk map of chunk information | ||||
|  */ | ||||
| public class FluidDiskMap { | ||||
| 
 | ||||
|     //The map of world position+chunk type to the file that actually houses that information | ||||
|     Map<String,String> worldPosFileMap = new HashMap<String,String>(); | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor | ||||
|      */ | ||||
|     public FluidDiskMap(){ | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets a key for a given chunk file based on a world coordinate | ||||
|      * @param worldX The x component | ||||
|      * @param worldY The y component | ||||
|      * @param worldZ The z component | ||||
|      * @return The key | ||||
|      */ | ||||
|     private static String getFluidChunkKey(int worldX, int worldY, int worldZ){ | ||||
|         return worldX + "_" + worldY + "_" + worldZ + "f"; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initializes a diskmap based on a given save name | ||||
|      * @param saveName The save name | ||||
|      */ | ||||
|     public void init(String saveName){ | ||||
|         LoggerInterface.loggerEngine.DEBUG("INIT CHUNK MAP " + saveName); | ||||
|         if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){ | ||||
|             worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "fluid.map", Map.class); | ||||
|             LoggerInterface.loggerEngine.DEBUG("POS FILE MAP: " + worldPosFileMap.keySet()); | ||||
|         } else { | ||||
|             worldPosFileMap = new HashMap<String,String>(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Saves the disk map to disk | ||||
|      */ | ||||
|     public void save(){ | ||||
|         FileUtils.serializeObjectToSavePath(Globals.currentSaveName, "fluid.map", worldPosFileMap); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the map contains a given chunk position | ||||
|      * @param worldX The x component | ||||
|      * @param worldY The y component | ||||
|      * @param worldZ The z component | ||||
|      * @return True if the map contains the chunk, false otherwise | ||||
|      */ | ||||
|     public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){ | ||||
|         return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the server fluid chunk from disk if it exists, otherwise returns null | ||||
|      * @param worldX The x coordinate | ||||
|      * @param worldY The y coordinate | ||||
|      * @param worldZ The z coordinate | ||||
|      * @return The server fluid chunk if it exists, null otherwise | ||||
|      */ | ||||
|     public ServerFluidChunk getFluidChunk(int worldX, int worldY, int worldZ){ | ||||
|         LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ); | ||||
|         ServerFluidChunk rVal = null; | ||||
|         if(containsFluidAtPosition(worldX, worldY, worldZ)){ | ||||
|             //read file | ||||
|             String fileName = worldPosFileMap.get(getFluidChunkKey(worldX, worldY, worldZ)); | ||||
|             byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSaveName, fileName); | ||||
|             //decompress | ||||
|             byte[] rawData = null; | ||||
|             ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||
|             InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out); | ||||
|             try { | ||||
|                 inflaterInputStream.write(rawDataCompressed); | ||||
|                 inflaterInputStream.flush(); | ||||
|                 inflaterInputStream.close(); | ||||
|                 rawData = out.toByteArray(); | ||||
|             } catch (IOException e) { | ||||
|                 // TODO Auto-generated catch block | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|             //parse | ||||
|             if(rawData != null){ | ||||
|                 ByteBuffer buffer = ByteBuffer.wrap(rawData); | ||||
|                 FloatBuffer floatView = buffer.asFloatBuffer(); | ||||
|                 int DIM = ServerTerrainChunk.CHUNK_DIMENSION; | ||||
|                 float[][][] weights = new float[DIM][DIM][DIM]; | ||||
|                 float[][][] velocityX = new float[DIM][DIM][DIM]; | ||||
|                 float[][][] velocityY = new float[DIM][DIM][DIM]; | ||||
|                 float[][][] velocityZ = new float[DIM][DIM][DIM]; | ||||
|                 for(int x = 0; x < DIM; x++){ | ||||
|                     for(int y = 0; y < DIM; y++){ | ||||
|                         for(int z = 0; z < DIM; z++){ | ||||
|                             weights[x][y][z] = floatView.get(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 for(int x = 0; x < DIM; x++){ | ||||
|                     for(int y = 0; y < DIM; y++){ | ||||
|                         for(int z = 0; z < DIM; z++){ | ||||
|                             velocityX[x][y][z] = floatView.get(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 for(int x = 0; x < DIM; x++){ | ||||
|                     for(int y = 0; y < DIM; y++){ | ||||
|                         for(int z = 0; z < DIM; z++){ | ||||
|                             velocityY[x][y][z] = floatView.get(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 for(int x = 0; x < DIM; x++){ | ||||
|                     for(int y = 0; y < DIM; y++){ | ||||
|                         for(int z = 0; z < DIM; z++){ | ||||
|                             velocityZ[x][y][z] = floatView.get(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 rVal = new ServerFluidChunk(worldX, worldY, worldZ, weights, velocityX, velocityY, velocityZ); | ||||
|             } | ||||
|         } | ||||
|         return rVal; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Saves a fluid chunk to disk | ||||
|      * @param fluidChunk The fluid chunk | ||||
|      */ | ||||
|     public void saveToDisk(ServerFluidChunk fluidChunk){ | ||||
|         LoggerInterface.loggerEngine.DEBUG("Save to disk: " + fluidChunk.getWorldX() + " " + fluidChunk.getWorldY() + " " + fluidChunk.getWorldZ()); | ||||
|         //get the file name for this chunk | ||||
|         String fileName = null; | ||||
|         String chunkKey = getFluidChunkKey(fluidChunk.getWorldX(),fluidChunk.getWorldY(),fluidChunk.getWorldZ()); | ||||
|         if(worldPosFileMap.containsKey(chunkKey)){ | ||||
|             fileName = worldPosFileMap.get(chunkKey); | ||||
|         } else { | ||||
|             fileName = chunkKey + ".dat"; | ||||
|         } | ||||
|         //generate binary for the file | ||||
|         float[][][] weights = fluidChunk.getWeights(); | ||||
|         float[][][] velocityX = fluidChunk.getVelocityX(); | ||||
|         float[][][] velocityY = fluidChunk.getVelocityY(); | ||||
|         float[][][] velocityZ = fluidChunk.getVelocityZ(); | ||||
|         int DIM = ServerTerrainChunk.CHUNK_DIMENSION; | ||||
|         ByteBuffer buffer = ByteBuffer.allocate(DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4); | ||||
|         FloatBuffer floatView = buffer.asFloatBuffer(); | ||||
|         for(int x = 0; x < DIM; x++){ | ||||
|             for(int y = 0; y < DIM; y++){ | ||||
|                 for(int z = 0; z < DIM; z++){ | ||||
|                     floatView.put(weights[x][y][z]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for(int x = 0; x < DIM; x++){ | ||||
|             for(int y = 0; y < DIM; y++){ | ||||
|                 for(int z = 0; z < DIM; z++){ | ||||
|                     floatView.put(velocityX[x][y][z]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for(int x = 0; x < DIM; x++){ | ||||
|             for(int y = 0; y < DIM; y++){ | ||||
|                 for(int z = 0; z < DIM; z++){ | ||||
|                     floatView.put(velocityY[x][y][z]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for(int x = 0; x < DIM; x++){ | ||||
|             for(int y = 0; y < DIM; y++){ | ||||
|                 for(int z = 0; z < DIM; z++){ | ||||
|                     floatView.put(velocityZ[x][y][z]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         //compress | ||||
|         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||
|         DeflaterOutputStream deflaterInputStream = new DeflaterOutputStream(out); | ||||
|         try { | ||||
|             deflaterInputStream.write(buffer.array()); | ||||
|             deflaterInputStream.flush(); | ||||
|             deflaterInputStream.close(); | ||||
|             //write to disk | ||||
|             FileUtils.saveBinaryToSavePath(Globals.currentSaveName, fileName, out.toByteArray()); | ||||
|             //save to the map of filenames | ||||
|             worldPosFileMap.put(chunkKey,fileName); | ||||
|         } catch (IOException e) { | ||||
|             // TODO Auto-generated catch block | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,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'"); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package electrosphere.server.fluid.generation; | ||||
| 
 | ||||
| import electrosphere.server.fluid.manager.ServerFluidChunk; | ||||
| import electrosphere.server.fluid.models.FluidModel; | ||||
| 
 | ||||
| /** | ||||
|  * Generates fluid | ||||
|  */ | ||||
| public interface FluidGenerator { | ||||
|      | ||||
|     /** | ||||
|      * Generates a chunk given an x, y, and z | ||||
|      * @param worldX The x component | ||||
|      * @param worldY The y component | ||||
|      * @param worldZ The z component | ||||
|      * @return The chunk | ||||
|      */ | ||||
|     public ServerFluidChunk generateChunk(int worldX, int worldY, int worldZ); | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the fluid model for the generation algorithm | ||||
|      * @param model The fluid model | ||||
|      */ | ||||
|     public void setModel(FluidModel model); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,129 @@ | ||||
| package electrosphere.server.fluid.manager; | ||||
| 
 | ||||
| 
 | ||||
| import org.joml.Vector3f; | ||||
| import org.joml.Vector3i; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Is a single chunk of terrain on the server | ||||
|  */ | ||||
| public class ServerFluidChunk { | ||||
| 
 | ||||
| 
 | ||||
|     int worldX, worldY, worldZ; | ||||
|     float[][][] weights; | ||||
|     float[][][] velocityX; | ||||
|     float[][][] velocityY; | ||||
|     float[][][] velocityZ; | ||||
| 
 | ||||
|     public ServerFluidChunk( | ||||
|         int worldX, | ||||
|         int worldY, | ||||
|         int worldZ, | ||||
|         float[][][] weights, | ||||
|         float[][][] velocityX, | ||||
|         float[][][] velocityY, | ||||
|         float[][][] velocityZ | ||||
|         ) { | ||||
|         this.worldX = worldX; | ||||
|         this.worldY = worldY; | ||||
|         this.worldZ = worldZ; | ||||
|         this.weights = weights; | ||||
|         this.velocityX = velocityX; | ||||
|         this.velocityY = velocityY; | ||||
|         this.velocityZ = velocityZ; | ||||
|     } | ||||
| 
 | ||||
|     public int getWorldX() { | ||||
|         return worldX; | ||||
|     } | ||||
| 
 | ||||
|     public int getWorldY() { | ||||
|         return worldY; | ||||
|     } | ||||
| 
 | ||||
|     public int getWorldZ() { | ||||
|         return worldZ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the world position of this terrain chunk as a joml Vector | ||||
|      * @return The vector | ||||
|      */ | ||||
|     public Vector3i getWorldPosition(){ | ||||
|         return new Vector3i(worldX,worldY,worldZ); | ||||
|     } | ||||
| 
 | ||||
|     public float[][][] getWeights() { | ||||
|         return weights; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the weight of a voxel at a poisiton | ||||
|      * @param localPosition The local position | ||||
|      * @return The weight of the specified voxel | ||||
|      */ | ||||
|     public float getWeight(Vector3i localPosition){ | ||||
|         return getWeight(localPosition.x,localPosition.y,localPosition.z); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the weight of a voxel at a poisiton | ||||
|      * @param x The x coordinate | ||||
|      * @param y The y coordinate | ||||
|      * @param z The z coordinate | ||||
|      * @return The weight of the specified voxel | ||||
|      */ | ||||
|     public float getWeight(int x, int y, int z){ | ||||
|         return weights[x][y][z]; | ||||
|     } | ||||
| 
 | ||||
|     //get velocity x | ||||
|     public float[][][] getVelocityX() { | ||||
|         return velocityX; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     //set velocity x | ||||
|     public void setVelocityX(float[][][] velocityX) { | ||||
|         this.velocityX = velocityX; | ||||
|     } | ||||
| 
 | ||||
|     //get velocity y | ||||
|     public float[][][] getVelocityY() { | ||||
|         return velocityY; | ||||
|     } | ||||
| 
 | ||||
|     //set velocity y | ||||
|     public void setVelocityY(float[][][] velocityY) { | ||||
|         this.velocityY = velocityY; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     //get velocity z | ||||
|     public float[][][] getVelocityZ() { | ||||
|         return velocityZ; | ||||
|     } | ||||
| 
 | ||||
|     //set velocity z | ||||
|     public void setVelocityZ(float[][][] velocityZ) { | ||||
|         this.velocityZ = velocityZ; | ||||
|     } | ||||
| 
 | ||||
|     //get a velocity at a given x, y and z as a Vector3f | ||||
|     public Vector3f getVelocity(int x, int y, int z){ | ||||
|         return new Vector3f(velocityX[x][y][z],velocityY[x][y][z],velocityZ[x][y][z]); | ||||
|     } | ||||
| 
 | ||||
|     //set a velocity at a given x, y, and z given three ints | ||||
|     public void setVelocity(int x, int y, int z, float velX, float velY, float velZ){ | ||||
|         velocityX[x][y][z] = velX; | ||||
|         velocityY[x][y][z] = velY; | ||||
|         velocityZ[x][y][z] = velZ; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,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 | ||||
|     } | ||||
|      | ||||
| } | ||||
							
								
								
									
										343
									
								
								src/main/java/electrosphere/server/fluid/models/FluidModel.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/main/java/electrosphere/server/fluid/models/FluidModel.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,343 @@ | ||||
| package electrosphere.server.fluid.models; | ||||
| 
 | ||||
| import electrosphere.util.annotation.Exclude; | ||||
| 
 | ||||
| public class FluidModel { | ||||
|      | ||||
|     int dynamicInterpolationRatio; | ||||
|     float interpolationRandomDampener = 0.4f; | ||||
|      | ||||
|     int discreteArrayDimension; | ||||
|     @Exclude | ||||
|     private float[][] elevation; | ||||
|      | ||||
|     float realMountainThreshold; | ||||
|     float realOceanThreshold; | ||||
|      | ||||
|     FluidModel() { | ||||
|     } | ||||
|      | ||||
|     public FluidModel( | ||||
|             int dimension, | ||||
|             int dynamicInterpolationRatio | ||||
|     ){ | ||||
|          | ||||
|         this.dynamicInterpolationRatio = dynamicInterpolationRatio; | ||||
|         this.discreteArrayDimension = dimension; | ||||
|     } | ||||
|      | ||||
|     public static FluidModel constructFluidModel(int dimension, int dynamicInterpolationRatio){ | ||||
|         FluidModel rVal = new FluidModel(); | ||||
|         rVal.discreteArrayDimension = dimension; | ||||
|         rVal.dynamicInterpolationRatio = dynamicInterpolationRatio; | ||||
|         return rVal; | ||||
|     } | ||||
|      | ||||
|     public float[][] getElevation(){ | ||||
|         return elevation; | ||||
|     } | ||||
|      | ||||
|     public void setInterpolationRandomDampener(float f){ | ||||
|         interpolationRandomDampener = f; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Dynamically interpolates a chunk of a specific size from the pre-existing elevation map | ||||
|      * @param x The x position on the elevation map to get a chunk from | ||||
|      * @param y The y position on the elevation map to get a chunk from | ||||
|      * @return Dynamically interpolated float array of elevations of chunk | ||||
|      */ | ||||
|     public float[][] getElevationForChunk(int x, int y){ | ||||
|          | ||||
|         //this is what we intend to return from the function | ||||
|         float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio]; | ||||
|          | ||||
|         /* | ||||
|         So we're looking at chunk x,y | ||||
|          | ||||
|         if this is our grid: | ||||
|          | ||||
|             4  0.1    0.2     0.3     0.4 | ||||
|         ^ | ||||
|         |   3  0.1    0.2     0.3     0.4 | ||||
|         | | ||||
|         |   2  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|         x   1  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|               0      1       2       3 | ||||
|          | ||||
|               y  ---- > | ||||
|          | ||||
|         say we're looking at x=2,y=1 | ||||
|          | ||||
|         "macroValues" should contain the values for bounds x = [1,3] and y = [0,2] | ||||
|          | ||||
|         the goal is to have the "center" of the output chunk have the value the | ||||
|         elevation grid at x=2,y=1 | ||||
|          | ||||
|         */ | ||||
|          | ||||
|          | ||||
|         //set macroValues | ||||
|         float[][] macroValues = getMacroValuesAtPosition(x,y); | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|         int halfLength = dynamicInterpolationRatio/2; | ||||
|          | ||||
|         /* | ||||
|          | ||||
|         Four quadrants we're generating | ||||
|          | ||||
|              _____________________ | ||||
|             |1         |2         | | ||||
|             |          |          | | ||||
|             |          |          | | ||||
|             |          |          | | ||||
|             |__________|__________| | ||||
|             |3         |4         | | ||||
|             |          |          | | ||||
|             |          |          | | ||||
|             |__________|__________| | ||||
|              | ||||
|         First set of loops is quadrant 1 | ||||
|         then quadrant 2 | ||||
|         then quadrant 3 | ||||
|         then quadrant 4 | ||||
|          | ||||
|         */ | ||||
|          | ||||
|         int outXOffset = 0; | ||||
|         int outYOffset = 0; | ||||
|          | ||||
|         for(int i = 0; i < halfLength; i++){ | ||||
|             for(int j = 0; j < halfLength; j++){ | ||||
|                 rVal[i+outXOffset][j+outYOffset] =  | ||||
|                         (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] + | ||||
|                         (1.0f * (0          - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + | ||||
|                         (1.0f * (halfLength - i) * (0          - j))/(halfLength * halfLength) * macroValues[0][1] + | ||||
|                         (1.0f * (0          - i) * (0          - j))/(halfLength * halfLength) * macroValues[1][1] | ||||
|                         ; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         outXOffset = halfLength; | ||||
|         for(int i = 0; i < halfLength; i++){ | ||||
|             for(int j = 0; j < halfLength; j++){ | ||||
|                 rVal[i+outXOffset][j+outYOffset] =  | ||||
|                         (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + | ||||
|                         (1.0f * (0          - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] + | ||||
|                         (1.0f * (halfLength - i) * (0          - j))/(halfLength * halfLength) * macroValues[1][1] + | ||||
|                         (1.0f * (0          - i) * (0          - j))/(halfLength * halfLength) * macroValues[2][1] | ||||
|                         ; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         outXOffset = 0; | ||||
|         outYOffset = halfLength; | ||||
|         for(int i = 0; i < halfLength; i++){ | ||||
|             for(int j = 0; j < halfLength; j++){ | ||||
|                 rVal[i+outXOffset][j+outYOffset] =  | ||||
|                         (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] + | ||||
|                         (1.0f * (0          - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + | ||||
|                         (1.0f * (halfLength - i) * (0          - j))/(halfLength * halfLength) * macroValues[0][2] + | ||||
|                         (1.0f * (0          - i) * (0          - j))/(halfLength * halfLength) * macroValues[1][2] | ||||
|                         ; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         outXOffset = halfLength; | ||||
|         for(int i = 0; i < halfLength; i++){ | ||||
|             for(int j = 0; j < halfLength; j++){ | ||||
|                 rVal[i+outXOffset][j+outYOffset] =  | ||||
|                         (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + | ||||
|                         (1.0f * (0          - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] + | ||||
|                         (1.0f * (halfLength - i) * (0          - j))/(halfLength * halfLength) * macroValues[1][2] + | ||||
|                         (1.0f * (0          - i) * (0          - j))/(halfLength * halfLength) * macroValues[2][2] | ||||
|                         ; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|          | ||||
|         return rVal; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     /* | ||||
|         So we're looking at chunk x,y | ||||
|          | ||||
|         if this is our grid: | ||||
|          | ||||
|             4  0.1    0.2     0.3     0.4 | ||||
|         ^ | ||||
|         |   3  0.1    0.2     0.3     0.4 | ||||
|         | | ||||
|         |   2  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|         x   1  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|               0      1       2       3 | ||||
|          | ||||
|               y  ---- > | ||||
|          | ||||
|         say we're looking at x=2,y=1 | ||||
|          | ||||
|         "macroValues" should contain the values for x = [1,3] and y = [0,2] | ||||
|          | ||||
|         the goal is to have the "center" of the output chunk have the value the | ||||
|         elevation grid at x=2,y=1 | ||||
|          | ||||
|         */ | ||||
|      | ||||
|     public float[][] getMacroValuesAtPosition(int x, int y){ | ||||
|          | ||||
|         float[][] rVal = new float[3][3]; | ||||
|         rVal[1][1] = elevation[x][y]; | ||||
|         if(x - 1 >= 0){ | ||||
|             rVal[0][1] = elevation[x-1][y]; | ||||
|             if(y - 1 >= 0){ | ||||
|                 rVal[0][0] = elevation[x-1][y-1]; | ||||
|             } else { | ||||
|                 rVal[0][0] = 0; | ||||
|             } | ||||
|             if(y + 1 < discreteArrayDimension){ | ||||
|                 rVal[0][2] = elevation[x-1][y+1]; | ||||
|             } else { | ||||
|                 rVal[0][2] = 0; | ||||
|             } | ||||
|         } else {  | ||||
|             rVal[0][0] = 0; | ||||
|             rVal[0][1] = 0; | ||||
|             rVal[0][2] = 0; | ||||
|         } | ||||
|         if(x + 1 < discreteArrayDimension){ | ||||
|             rVal[2][1] = elevation[x+1][y]; | ||||
|             if(y - 1 >= 0){ | ||||
|                 rVal[2][0] = elevation[x+1][y-1]; | ||||
|             } else { | ||||
|                 rVal[2][0] = 0; | ||||
|             } | ||||
|             if(y + 1 < discreteArrayDimension){ | ||||
|                 rVal[2][2] = elevation[x+1][y+1]; | ||||
|             } else { | ||||
|                 rVal[2][2] = 0; | ||||
|             } | ||||
|         } else { | ||||
|             rVal[2][0] = 0; | ||||
|             rVal[2][1] = 0; | ||||
|             rVal[2][2] = 0; | ||||
|         } | ||||
|         if(y - 1 >= 0){ | ||||
|             rVal[1][0] = elevation[x][y-1]; | ||||
|         } else { | ||||
|             rVal[1][0] = 0; | ||||
|         } | ||||
|         if(y + 1 < discreteArrayDimension){ | ||||
|             rVal[1][2] = elevation[x][y+1]; | ||||
|         } else { | ||||
|             rVal[1][2] = 0; | ||||
|         } | ||||
|          | ||||
|         return rVal; | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|     public float[][] getRad5MacroValuesAtPosition(int x, int y){ | ||||
|          | ||||
|         float[][] rVal = new float[5][5]; | ||||
|         for(int i = -2; i < 3; i++){ | ||||
|             for(int j = -2; j < 3; j++){ | ||||
|                 if(x + i >= 0 && x + i < discreteArrayDimension && y + j >= 0 && y + j < discreteArrayDimension){ | ||||
|                     rVal[i+2][j+2] = elevation[x+i][y+j]; | ||||
|                 } else { | ||||
|                     rVal[i+2][j+2] = 0; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return rVal; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     /* | ||||
|         So we're looking at chunk x,y | ||||
|          | ||||
|         if this is our grid: | ||||
|          | ||||
|             4  0.1    0.2     0.3     0.4 | ||||
|         ^ | ||||
|         |   3  0.1    0.2     0.3     0.4 | ||||
|         | | ||||
|         |   2  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|         x   1  0.1    0.2     0.3     0.4 | ||||
|          | ||||
|               0      1       2       3 | ||||
|          | ||||
|               y  ---- > | ||||
|          | ||||
|         say we're looking at x=2,y=1 | ||||
|          | ||||
|         "macroValues" should contain the values for x = [1,3] and y = [0,2] | ||||
|          | ||||
|         the goal is to have the "center" of the output chunk have the value the | ||||
|         elevation grid at x=2,y=1 | ||||
|          | ||||
|         */ | ||||
|      | ||||
|      | ||||
|     public float getRandomDampener(){ | ||||
|         return interpolationRandomDampener; | ||||
|     } | ||||
|      | ||||
|     public int getDynamicInterpolationRatio(){ | ||||
|         return dynamicInterpolationRatio; | ||||
|     } | ||||
| 
 | ||||
|     public float getRealMountainThreshold() { | ||||
|         return realMountainThreshold; | ||||
|     } | ||||
| 
 | ||||
|     public float getRealOceanThreshold() { | ||||
|         return realOceanThreshold; | ||||
|     } | ||||
|      | ||||
|     public String getModificationKey(int x, int y, int z){ | ||||
|         return x + "_" + y + "_" + z; | ||||
|     } | ||||
|      | ||||
| //     public void addModification(TerrainModification modification){ | ||||
| //         String key = getModificationKey(modification.getWorldPos().x,modification.getWorldPos().y,modification.getWorldPos().z); | ||||
| //         ModificationList list; | ||||
| //         if(!modifications.containsKey(key)){ | ||||
| //             list = new ModificationList(); | ||||
| //             modifications.put(key, list); | ||||
| //         } else { | ||||
| //             list = modifications.get(key); | ||||
| //         } | ||||
| //         list.addModification(modification); | ||||
| //     } | ||||
|      | ||||
| //     public boolean containsModificationsAtCoord(int worldX, int worldY, int worldZ){ | ||||
| //         return modifications.containsKey(getModificationKey(worldX, worldY, worldZ)); | ||||
| //     } | ||||
|      | ||||
| //     public ModificationList getModifications(int worldX, int worldY, int worldZ){ | ||||
| // //        System.out.println("Got modifications at " + worldX + " " + worldY); | ||||
| //         return modifications.get(getModificationKey(worldX, worldY, worldZ)); | ||||
| //     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the elevation array (For instance when read from save file on loading a save) | ||||
|      * @param elevation The elevation array to set to | ||||
|      */ | ||||
|     public void setElevationArray(float[][] elevation){ | ||||
|         this.elevation = elevation; | ||||
|     } | ||||
|      | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user