package electrosphere.client.fluid.cells; import org.joml.Vector3d; import org.joml.Vector3i; import org.ode4j.ode.DBody; import electrosphere.client.fluid.cache.FluidChunkData; import electrosphere.collision.CollisionEngine; import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.fluid.FluidChunk; import electrosphere.renderer.shader.ShaderProgram; import electrosphere.renderer.texture.Texture; import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * * @author satellite */ public class FluidCell { //the position of the draw cell in world coordinates Vector3i worldPos; FluidChunkData data; Entity modelEntity; ShaderProgram program; DBody physicsObject; float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; //the value of an empty fluid cell weight that is not neighbored by a fluid value public static final float ISO_SURFACE_EMPTY = -1; FluidCell(){ } /** * Constructs a drawcell object */ public static FluidCell generateFluidCell( Vector3i worldPos, FluidChunkData data, ShaderProgram program ){ FluidCell rVal = new FluidCell(); rVal.worldPos = worldPos; rVal.program = program; rVal.data = data; return rVal; } /** * Generates a drawable entity based on this chunk */ public void generateDrawableEntity(){ if(modelEntity != null){ Globals.clientScene.deregisterEntity(modelEntity); } fillInData(); modelEntity = FluidChunk.clientCreateFluidChunkEntity(weights); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos()); } protected Vector3d getRealPos(){ return new Vector3d( worldPos.x * FluidChunkData.CHUNK_SIZE, worldPos.y * FluidChunkData.CHUNK_SIZE, worldPos.z * FluidChunkData.CHUNK_SIZE ); } /** * Destroys a drawcell including its physics */ public void destroy(){ CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); collisionEngine.destroyEntityThatHasPhysics(modelEntity); EntityUtils.cleanUpEntity(modelEntity); //destruct model String modelPath = (String)modelEntity.getData(EntityDataStrings.DATA_STRING_MODEL_PATH); Globals.assetManager.deregisterModelPath(modelPath); } /** * Gets the current chunk data for this draw cell * @return The chunk data */ public FluidChunkData getData(){ return data; } /** * Fills in the internal arrays of data for generate terrain models */ private void fillInData(){ // //fill in data // //main chunk FluidChunkData currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos); for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ weights[x][y][z] = currentChunk.getWeight(x,y,z); } } } //face X if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = currentChunk.getWeight(0, i, j); } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = ISO_SURFACE_EMPTY; } } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = ISO_SURFACE_EMPTY; } } } //face Y if(worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = currentChunk.getWeight(i, 0, j); } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = ISO_SURFACE_EMPTY; } } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = ISO_SURFACE_EMPTY; } } } //face Z if(worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z + 1); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, j, 0); } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){ weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } } //edge X-Y if( worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() ){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = currentChunk.getWeight(0, 0, i); } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = ISO_SURFACE_EMPTY; } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = ISO_SURFACE_EMPTY; } } //edge X-Z if( worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() ){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z + 1); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, i, 0); } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } //edge Y-Z if( worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() ){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z + 1); if(currentChunk != null){ for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, 0, 0); } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } } else { for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){ weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } if( worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() ){ currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1); if(currentChunk != null){ weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, 0, 0); } else { weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } } else { weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = ISO_SURFACE_EMPTY; } //now set neighboring air weights based on nearby fluid count //idea being that we dont have the snapping behavior from iso surface jumping from -1->0.01 int[] neighborIndexX = new int[]{-1,1,0,0,0,0}; int[] neighborIndexY = new int[]{0,0,-1,1,0,0}; int[] neighborIndexZ = new int[]{0,0,0,0,-1,1}; for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){ for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){ if(weights[x][y][z] > 0){ continue; } for(int i = 0; i < 6; i++){ int currX = x + neighborIndexX[i]; int currY = y + neighborIndexY[i]; int currZ = z + neighborIndexZ[i]; if( currX >= 0 && currX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE && currY >= 0 && currY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE && currZ >= 0 && currZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE && (1 + weights[x][y][z]) < weights[currX][currY][currZ] ){ weights[x][y][z] = -(1 - weights[currX][currY][currZ]); if(weights[x][y][z] >= 0){ weights[x][y][z] = -0.01f; } } } } } } } }