diff --git a/assets/Shaders/fluid1/fluid1.fs b/assets/Shaders/fluid1/fluid1.fs new file mode 100644 index 00000000..1e59f1a4 --- /dev/null +++ b/assets/Shaders/fluid1/fluid1.fs @@ -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; +} \ No newline at end of file diff --git a/assets/Shaders/fluid1/fluid1.vs b/assets/Shaders/fluid1/fluid1.vs new file mode 100644 index 00000000..25f6219c --- /dev/null +++ b/assets/Shaders/fluid1/fluid1.vs @@ -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; +} diff --git a/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java b/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java new file mode 100644 index 00000000..fc45cc96 --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java @@ -0,0 +1,82 @@ +package electrosphere.client.fluid.cache; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Acts as a cache in front of fluid model to streamline receiving chunks + */ +public class ClientFluidCache { + + //cache capacity + int cacheSize; + //the map of chunk key -> chunk data + Map cacheMap = new ConcurrentHashMap(); + //the list of keys in the cache + List cacheList = new CopyOnWriteArrayList(); + + /** + * Constructor + * @param cacheSize The capacity of the cache + */ + public ClientFluidCache(int cacheSize){ + this.cacheSize = cacheSize; + } + + /** + * Adds a chunk data to the fluid cache + * @param worldX The x world position + * @param worldY The y world position + * @param worldZ The z world position + * @param chunkData The chunk data to add at the specified positions + */ + public void addChunkDataToCache(int worldX, int worldY, int worldZ, FluidChunkData chunkData){ + cacheMap.put(getKey(worldX,worldY,worldZ),chunkData); + while(cacheList.size() > cacheSize){ + String currentChunk = cacheList.remove(0); + cacheMap.remove(currentChunk); + } + } + + + /** + * Generates a key for the cache based on the position provided + * @param worldX The x world position + * @param worldY The y world position + * @param worldZ The z world position + * @return The cache key + */ + public String getKey(int worldX, int worldY, int worldZ){ + return worldX + "_" + worldY + "_" + worldZ; + } + + /** + * Checks whether the cache contains chunk data at a given world point + * @param worldX The x world position + * @param worldY The y world position + * @param worldZ The z world position + * @return True if the cache contains chunk data at the specified point, false otherwise + */ + public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ + return cacheMap.containsKey(getKey(worldX,worldY,worldZ)); + } + + + + + + /** + * Gets chunk data at the given world point + * @param worldX The x world position + * @param worldY The y world position + * @param worldZ The z world position + * @return The chunk data if it exists, null otherwise + */ + public FluidChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){ + return cacheMap.get(getKey(worldX,worldY,worldZ)); + } + +} diff --git a/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java b/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java new file mode 100644 index 00000000..3378228c --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java @@ -0,0 +1,160 @@ +package electrosphere.client.fluid.cache; + +import java.util.HashSet; +import java.util.Set; + +import org.joml.Vector3i; + +import electrosphere.server.terrain.manager.ServerTerrainChunk; + +/** + * A container of data about a chunk of fluid + */ +public class FluidChunkData { + + //The size of a chunk in virtual data + public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION; + //The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk + public static final int CHUNK_DATA_GENERATOR_SIZE = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; + + //What type of fluid is in this voxel, eg stone vs dirt vs grass, etc + int[][][] voxelType; + //How much of that fluid type is in this voxel + float[][][] voxelWeight; + + //the list of positions modified since the last call to resetModifiedPositions + //Used in DrawCell to keep track of which positions to invalidate + Set modifiedSinceLastGeneration = new HashSet(); + + + /** + * Gets the voxel type array in this container + * @return The voxel type array + */ + public int[][][] getVoxelType(){ + return voxelType; + } + + /** + * Sets the voxel type array in this container + * @param voxelType The voxel type array + */ + public void setVoxelType(int[][][] voxelType){ + //mark changed cells + if(this.voxelType != null){ + for(int x = 0; x < CHUNK_SIZE; x++){ + for(int y = 0; y < CHUNK_SIZE; y++){ + for(int z = 0; z < CHUNK_SIZE; z++){ + if(voxelType[x][y][z] != this.voxelType[x][y][z]){ + String key = getVoxelPositionKey(new Vector3i(x,y,z)); + if(!modifiedSinceLastGeneration.contains(key)){ + modifiedSinceLastGeneration.add(key); + } + } + } + } + } + } + //update data + this.voxelType = voxelType; + } + + /** + * Gets the voxel weight array in this container + * @return The voxel weight array + */ + public float[][][] getVoxelWeight(){ + return voxelWeight; + } + + /** + * Sets the voxel weight array in this container + * @param voxelWeight The voxel weight array + */ + public void setVoxelWeight(float[][][] voxelWeight){ + //mark changed cells + if(this.voxelWeight != null){ + for(int x = 0; x < CHUNK_SIZE; x++){ + for(int y = 0; y < CHUNK_SIZE; y++){ + for(int z = 0; z < CHUNK_SIZE; z++){ + if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){ + String key = getVoxelPositionKey(new Vector3i(x,y,z)); + if(!modifiedSinceLastGeneration.contains(key)){ + modifiedSinceLastGeneration.add(key); + } + } + } + } + } + } + //update data + this.voxelWeight = voxelWeight; + } + + /** + * Updates the value of a single voxel in the chunk + * @param localX The local position X + * @param localY The local position Y + * @param localZ The local position Z + * @param weight The weight to set it to + * @param type The type to set the voxel to + */ + public void updatePosition(int localX, int localY, int localZ, float weight, int type){ + voxelWeight[localX][localY][localZ] = weight; + voxelType[localX][localY][localZ] = type; + //store as modified in cache + String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ)); + if(!modifiedSinceLastGeneration.contains(key)){ + modifiedSinceLastGeneration.add(key); + } + } + + /** + * Gets the weight of a voxel at a poisiton + * @param localPosition The local position + * @return The weight of the specified voxel + */ + public float getWeight(Vector3i localPosition){ + return voxelWeight[localPosition.x][localPosition.y][localPosition.z]; + } + + /** + * Gets the type of a voxel at a position + * @param localPosition The local position + * @return The type of the specified voxel + */ + public int getType(Vector3i localPosition){ + return voxelType[localPosition.x][localPosition.y][localPosition.z]; + } + + /** + * Resets the cache of modified positions + */ + public void resetModifiedPositions(){ + this.modifiedSinceLastGeneration.clear(); + } + + /** + * Gets the set of all modified positions since the last call to resetModifiedPositions + * @return The set of all modified positions + */ + public Set getModifiedPositions(){ + Set rVal = new HashSet(); + for(String key : modifiedSinceLastGeneration){ + String[] split = key.split("_"); + rVal.add(new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2]))); + } + return rVal; + } + + /** + * Gets a key for the modifiedSinceLastGeneration set based on a voxel position + * @param position The voxel position + * @return The key + */ + private String getVoxelPositionKey(Vector3i position){ + return position.x + "_" + position.y + "_" + position.z; + } + + +} diff --git a/src/main/java/electrosphere/client/fluid/cells/DrawCellManager.java b/src/main/java/electrosphere/client/fluid/cells/DrawCellManager.java new file mode 100644 index 00000000..e6813cb9 --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/cells/DrawCellManager.java @@ -0,0 +1,414 @@ +package electrosphere.client.fluid.cells; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.joml.Vector3d; +import org.joml.Vector3i; + +import electrosphere.client.terrain.cache.ChunkData; +import electrosphere.client.terrain.manager.ClientTerrainManager; +import electrosphere.engine.Globals; +import electrosphere.entity.EntityUtils; +import electrosphere.net.parser.net.message.TerrainMessage; +import electrosphere.renderer.ShaderProgram; + +/** + * + * @author satellite + */ +public class DrawCellManager { + + + //the center of this cell manager's array in cell space + int cellX; + int cellY; + int cellZ; + + + //the dimensions of the world that this cell manager can handles + int cellWidth; + + //the width of a minicell in this manager + int miniCellWidth; + + //all currently displaying mini cells + Set cells; + Map keyCellMap = new HashMap(); + Set hasNotRequested; + Set hasRequested; + Set drawable; + Set undrawable; + Set updateable; + + + ShaderProgram program; + + + + // int drawRadius = 5; + int drawStepdownInterval = 3; + int drawStepdownValue = 25; + + double drawRadius = 200; + + int physicsRadius = 3; + + int worldBoundDiscreteMin = 0; + int worldBoundDiscreteMax = 0; + + //client terrain manager + // ClientTerrainManager clientTerrainManager; + + + //ready to start updating? + boolean update = false; + + //controls whether we try to generate the drawable entities + //we want this to be false when in server-only mode + boolean generateDrawables = false; + + + /** + * DrawCellManager constructor + * @param commonWorldData The common world data + * @param clientTerrainManager The client terrain manager + * @param discreteX The initial discrete position X coordinate + * @param discreteY The initial discrete position Y coordinate + */ + public DrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){ + worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f); + cells = new HashSet(); + hasNotRequested = new HashSet(); + drawable = new HashSet(); + undrawable = new HashSet(); + updateable = new HashSet(); + hasRequested = new HashSet(); + + cellX = discreteX; + cellY = discreteY; + cellZ = discreteZ; + + program = Globals.terrainShaderProgram; + + // drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius(); + drawStepdownInterval = Globals.userSettings.getGameplayPhysicsCellRadius(); + physicsRadius = Globals.userSettings.getGameplayPhysicsCellRadius(); + + invalidateAllCells(); + + update = true; + } + + DrawCellManager(){ + + } + + public void setCell(Vector3i cellPos){ + cellX = cellPos.x; + cellY = cellPos.y; + cellZ = cellPos.z; + } + + void updateUnrequestedCell(){ + if(hasNotRequested.size() > 0){ + String targetKey = hasNotRequested.iterator().next(); + hasNotRequested.remove(targetKey); + Vector3i worldPos = getVectorFromKey(targetKey); + // Vector3i vector = getVectorFromKey(targetKey); + // int currentCellX = cellX - drawRadius + vector.x; + // int currentCellY = cellY - drawRadius + vector.y; + // int currentCellZ = cellZ - drawRadius + vector.z; + + if( + worldPos.x >= 0 && + worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.y >= 0 && + worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.z >= 0 && + worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() + ){ + // if(!hasRequested.contains(targetKey)){ + //client should request chunk data from server + Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage( + worldPos.x, + worldPos.y, + worldPos.z + )); + undrawable.add(targetKey); + hasRequested.add(targetKey); + // } + } + } + } + + /** + * Makes one of the undrawable cells drawable + */ + void makeCellDrawable(){ + + if(undrawable.size() > 0){ + String targetKey = undrawable.iterator().next(); + Vector3i worldPos = getVectorFromKey(targetKey); + if( + worldPos.x >= 0 && + worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.y >= 0 && + worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.z >= 0 && + worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() + ){ + if(containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){ + FluidDrawCell cell = FluidDrawCell.generateFluidCell( + worldPos, + Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z), + program + ); + cells.add(cell); + keyCellMap.put(targetKey,cell); + // undrawable.add(targetKey); + undrawable.remove(targetKey); + drawable.add(targetKey); + //make drawable entity + keyCellMap.get(targetKey).generateDrawableEntity(); + //evaluate for foliage + Globals.clientFoliageManager.evaluateChunk(worldPos); + } + } + } + } + + /** + * Updates a cell that can be updated + */ + void updateCellModel(){ + if(updateable.size() > 0){ + String targetKey = updateable.iterator().next(); + updateable.remove(targetKey); + Vector3i worldPos = getVectorFromKey(targetKey); + if( + worldPos.x >= 0 && + worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.y >= 0 && + worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.z >= 0 && + worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() + ){ +// if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){ +// needsPhysics[targetX][targetY] = true; +// } + // int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); + // int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); + // while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){ + // stride = stride + 1; + // } + keyCellMap.get(targetKey).destroy(); + keyCellMap.get(targetKey).generateDrawableEntity(); + } + drawable.add(targetKey); + } + } + + + public boolean containsUnrequestedCell(){ + return hasNotRequested.size() > 0; + } + + public boolean containsUndrawableCell(){ + return undrawable.size() > 0; + } + + public boolean containsUpdateableCell(){ + return updateable.size() > 0; + } + + + + + public int transformRealSpaceToCellSpace(double input){ + return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio()); + } + + /** + * Clears the valid set and adds all keys to invalid set + */ + public void invalidateAllCells(){ + drawable.clear(); + hasNotRequested.clear(); + clearOutOfBoundsCells(); + queueNewCells(); + } + + /** + * Calculates whether the position of the player has changed and if so, invalidates and cleans up cells accordingly + * @param position The position of the player entity on current frame + */ + public void calculateDeltas(Vector3d position){ + //check if any not requested cells no longer need to be requested + clearOutOfBoundsCells(); + //check if any cells should be added + queueNewCells(); + } + + /** + * Clears all cells outside of draw radius + */ + private void clearOutOfBoundsCells(){ + Set cellsToRemove = new HashSet(); + for(FluidDrawCell cell : cells){ + Vector3d realPos = cell.getRealPos(); + if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){ + cellsToRemove.add(cell); + } + } + for(FluidDrawCell cell : cellsToRemove){ + cells.remove(cell); + String key = getCellKey(cell.worldPos.x, cell.worldPos.y, cell.worldPos.z); + hasNotRequested.remove(key); + drawable.remove(key); + undrawable.remove(key); + updateable.remove(key); + keyCellMap.remove(key); + hasRequested.remove(key); + } + } + + /** + * Queues new cells that are in bounds but not currently accounted for + */ + private void queueNewCells(){ + if(Globals.playerEntity != null && Globals.clientWorldData != null){ + Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); + for(int x = -(int)drawRadius; x < drawRadius; x = x + ChunkData.CHUNK_SIZE){ + for(int y = -(int)drawRadius; y < drawRadius; y = y + ChunkData.CHUNK_SIZE){ + for(int z = -(int)drawRadius; z < drawRadius; z = z + ChunkData.CHUNK_SIZE){ + Vector3d newPos = new Vector3d(playerPos.x + x, playerPos.y + y, playerPos.z + z); + Vector3i worldPos = new Vector3i( + Globals.clientWorldData.convertRealToChunkSpace(newPos.x), + Globals.clientWorldData.convertRealToChunkSpace(newPos.y), + Globals.clientWorldData.convertRealToChunkSpace(newPos.z) + ); + Vector3d chunkRealSpace = new Vector3d( + Globals.clientWorldData.convertChunkToRealSpace(worldPos.x), + Globals.clientWorldData.convertChunkToRealSpace(worldPos.y), + Globals.clientWorldData.convertChunkToRealSpace(worldPos.z) + ); + if( + playerPos.distance(chunkRealSpace) < drawRadius && + worldPos.x >= 0 && + worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.y >= 0 && + worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() && + worldPos.z >= 0 && + worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() + ){ + String key = getCellKey( + Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.x), + Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.y), + Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.z) + ); + if(!keyCellMap.containsKey(key) && !hasNotRequested.contains(key) && !undrawable.contains(key) && !drawable.contains(key) && + !hasRequested.contains(key)){ + hasNotRequested.add(key); + } + } + } + } + } + } + } + + /** + * Updates cells that need updating in this manager + */ + public void update(){ + if(update){ + if(containsUnrequestedCell() && !containsUndrawableCell()){ + updateUnrequestedCell(); + } else if(containsUndrawableCell()){ + makeCellDrawable(); + } else if(containsUpdateableCell()){ + updateCellModel(); + } + } + } + + /** + * Splits a cell key into its constituent coordinates in array format. + * @param cellKey The cell key to split + * @return The coordinates in array format + */ + // private int[] splitKeyToCoordinates(String cellKey){ + // int[] rVal = new int[3]; + // String[] components = cellKey.split("_"); + // for(int i = 0; i < 3; i++){ + // rVal[i] = Integer.parseInt(components[i]); + // } + // return rVal; + // } + + public boolean coordsInPhysicsSpace(int worldX, int worldY){ + return worldX <= cellX + physicsRadius && worldX >= cellX - physicsRadius && worldY <= cellY + physicsRadius && worldY >= cellY - physicsRadius; + } + + public void setGenerateDrawables(boolean generate){ + this.generateDrawables = generate; + } + + boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ + if(Globals.clientTerrainManager != null){ + return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); + } + return true; + } + + /** + * Gets the chunk data at a given point + * @param worldX The world position x component of the cell + * @param worldY The world position y component of the cell + * @param worldZ The world position z component of the cell + * @return The chunk data at the specified points + */ + ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){ + return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ); + } + + + /** + * Gets a unique key for the cell + * @param worldX The world position x component of the cell + * @param worldY The world position y component of the cell + * @param worldZ The world position z component of the cell + * @return The key + */ + private String getCellKey(int worldX, int worldY, int worldZ){ + return worldX + "_" + worldY + "_" + worldZ; + } + + /** + * Parses a vector3i from the cell key + * @param key The cell key + * @return The vector3i containing the components of the cell key + */ + private Vector3i getVectorFromKey(String key){ + String[] keyComponents = key.split("_"); + return new Vector3i(Integer.parseInt(keyComponents[0]),Integer.parseInt(keyComponents[1]),Integer.parseInt(keyComponents[2])); + } + + /** + * Marks a data cell as updateable (can be regenerated with a new model because the underlying data has changed) + * @param chunkX The chunk x coordinate + * @param chunkY The chunk y coordinate + * @param chunkZ The chunk z coordinate + */ + public void markUpdateable(int chunkX, int chunkY, int chunkZ){ + updateable.add(getCellKey(chunkX, chunkY, chunkZ)); + } + + + +// public + +} diff --git a/src/main/java/electrosphere/client/fluid/cells/FluidDrawCell.java b/src/main/java/electrosphere/client/fluid/cells/FluidDrawCell.java new file mode 100644 index 00000000..7ddfab15 --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/cells/FluidDrawCell.java @@ -0,0 +1,108 @@ +package electrosphere.client.fluid.cells; + +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.ode4j.ode.DBody; + +import electrosphere.client.fluid.cache.FluidChunkData; +import electrosphere.collision.CollisionEngine; +import electrosphere.engine.Globals; +import electrosphere.entity.ClientEntityUtils; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.fluid.FluidChunk; +import electrosphere.renderer.ShaderProgram; +import electrosphere.renderer.texture.Texture; + +/** + * + * @author satellite + */ +public class FluidDrawCell { + //the position of the draw cell in world coordinates + Vector3i worldPos; + + FluidChunkData data; + + Entity modelEntity; + + ShaderProgram program; + + DBody physicsObject; + + static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png"); + static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); + static Texture groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); + static Texture groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); + + static { +// groundTextureOne = new Texture("/Textures/Ground/GrassTileable.png"); +// groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); +// groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); +// groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); + } + + + FluidDrawCell(){ + + } + + + /** + * Constructs a drawcell object + */ + public static FluidDrawCell generateFluidCell( + Vector3i worldPos, + FluidChunkData data, + ShaderProgram program + ){ + FluidDrawCell rVal = new FluidDrawCell(); + rVal.worldPos = worldPos; + rVal.program = program; + rVal.data = data; + System.out.println("Create cell"); + 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()); + for(Vector3i position : data.getModifiedPositions()){ + Globals.clientFoliageManager.invalidateCell(worldPos, position); + } + data.resetModifiedPositions(); + + 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; + } + +} diff --git a/src/main/java/electrosphere/client/fluid/editing/FluidEditing.java b/src/main/java/electrosphere/client/fluid/editing/FluidEditing.java new file mode 100644 index 00000000..ae4456fe --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/editing/FluidEditing.java @@ -0,0 +1,71 @@ +package electrosphere.client.fluid.editing; + +import org.joml.Vector3d; +import org.joml.Vector3i; + +import electrosphere.client.terrain.cache.ChunkData; +import electrosphere.engine.Globals; +import electrosphere.net.parser.net.message.TerrainMessage; + +/** + * Utilities for editing fluid from client side of things + */ +public class FluidEditing { + + /** + * Performs a fluid chunk edit. Basically has a sphere around the provided position that it attempts to add value to. + * @param position The position to perform the edit + * @param editMagnitude The magnitude of the edit to perform + * @param type The type of block to make all edited blocks + * @param weight The weight of the sphere to apply the edit to + */ + public static void editFluid(Vector3d position, float editMagnitude, int type, float weight){ + if(position != null){ + // Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestUseTerrainPaletteMessage(position.x, position.y, position.z, editMagnitude, weight, type)); + //calculate kernel size + // int numPlacesToCheck = (int)((editMagnitude * 2 + 1) * (editMagnitude * 2 + 1) * (editMagnitude * 2 + 1)); + // //create and fill in kernel of positions to check + // int[] xOffsetSet = new int[numPlacesToCheck]; + // int[] yOffsetSet = new int[numPlacesToCheck]; + // int[] zOffsetSet = new int[numPlacesToCheck]; + // int i = 0; + // for(int x = -(int)editMagnitude; x <= (int)editMagnitude; x++){ + // for(int y = -(int)editMagnitude; y <= (int)editMagnitude; y++){ + // for(int z = -(int)editMagnitude; z <= (int)editMagnitude; z++){ + // xOffsetSet[i] = x; + // yOffsetSet[i] = y; + // zOffsetSet[i] = z; + // i++; + // } + // } + // } + // for(i = 0; i < numPlacesToCheck; i++){ + // //calculate position of edit + // Vector3d offsetPos = new Vector3d(position).add(xOffsetSet[i],yOffsetSet[i],zOffsetSet[i]); + // Vector3i chunkPos = Globals.clientWorldData.convertRealToChunkSpace(offsetPos); + // Vector3i voxelPos = Globals.clientWorldData.convertRealToVoxelSpace(offsetPos); + // //get distance from true center point of sphere to current voxel position in world space + // float distance = (float)new Vector3d(Math.floor(offsetPos.x),Math.floor(offsetPos.y),Math.floor(offsetPos.z)).distance(position); + // float currentPositionMagnitude = editMagnitude - distance; + // ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkPos.x, chunkPos.y, chunkPos.z); + // if( + // voxelPos.x < ChunkData.CHUNK_SIZE && + // voxelPos.y < ChunkData.CHUNK_SIZE && + // voxelPos.z < ChunkData.CHUNK_SIZE && + // currentPositionMagnitude > 0 && + // data != null + // ){ + // float current = data.getVoxelWeight()[voxelPos.x][voxelPos.y][voxelPos.z]; + // //hard clamp so it doesn't go over 1 + // float finalValue = Math.max(Math.min(current + weight,1),-1); + // Globals.clientTerrainManager.updateChunk( + // chunkPos.x, chunkPos.y, chunkPos.z, + // voxelPos.x, voxelPos.y, voxelPos.z, + // finalValue, 1 + // ); + // } + // } + } + } + +} diff --git a/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java new file mode 100644 index 00000000..ff9099b2 --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java @@ -0,0 +1,163 @@ +package electrosphere.client.fluid.manager; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.joml.Vector3i; + +import electrosphere.client.fluid.cache.ClientFluidCache; +import electrosphere.client.fluid.cache.FluidChunkData; +import electrosphere.client.scene.ClientWorldData; +import electrosphere.engine.Globals; +import electrosphere.entity.types.fluid.FluidChunkModelData; +import electrosphere.logger.LoggerInterface; +import electrosphere.net.parser.net.message.TerrainMessage; +import electrosphere.renderer.Model; +import electrosphere.renderer.meshgen.FluidChunkModelGeneration; +import electrosphere.server.terrain.manager.ServerTerrainManager; + +/** + * Manages fluid storage and access on the client + */ +public class ClientFluidManager { + + //queues messages from server + List messageQueue = new CopyOnWriteArrayList(); + + //The interpolation ratio of fluid + public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; + + //caches chunks from server + static final int CACHE_SIZE = 50; + + //used for caching the macro values + ClientFluidCache fluidCache; + + //The world data for the client + ClientWorldData clientWorldData; + + //The queue of fluid chunk data to be buffered to gpu + static List fluidChunkGenerationQueue = new CopyOnWriteArrayList(); + + /** + * Constructor + */ + public ClientFluidManager(){ + fluidCache = new ClientFluidCache(CACHE_SIZE); + } + + + public void handleMessages(){ + List bouncedMessages = new LinkedList(); + for(TerrainMessage message : messageQueue){ + messageQueue.remove(message); + switch(message.getMessageSubtype()){ + case SENDCHUNKDATA: { + int[][][] values = new int[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE]; + float[][][] weights = new float[FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE][FluidChunkData.CHUNK_DATA_GENERATOR_SIZE]; + ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData()); + FloatBuffer floatBuffer = buffer.asFloatBuffer(); + for(int x = 0; x < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){ + weights[x][y][z] = floatBuffer.get(); + } + } + } + IntBuffer intView = buffer.asIntBuffer(); + intView.position(floatBuffer.position()); + for(int x = 0; x < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){ + values[x][y][z] = intView.get(); + } + } + } + FluidChunkData data = new FluidChunkData(); + data.setVoxelType(values); + data.setVoxelWeight(weights); + fluidCache.addChunkDataToCache( + message.getworldX(), message.getworldY(), message.getworldZ(), + data + ); + } break; + default: + LoggerInterface.loggerEngine.WARNING("ClientFluidManager: unhandled network message of type" + message.getMessageSubtype()); + break; + } + } + for(TerrainMessage message : bouncedMessages){ + messageQueue.add(message); + } + } + + public void attachTerrainMessage(TerrainMessage message){ + messageQueue.add(message); + } + + public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ + return fluidCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ); + } + + public boolean containsChunkDataAtRealPoint(double x, double y, double z){ + assert clientWorldData != null; + return fluidCache.containsChunkDataAtWorldPoint( + clientWorldData.convertRealToChunkSpace(x), + clientWorldData.convertRealToChunkSpace(y), + clientWorldData.convertRealToChunkSpace(z) + ); + } + + /** + * Gets the chunk data at a given world position + * @param worldX The x component of the world coordinate + * @param worldY The y component of the world coordinate + * @param worldZ The z component of the world coordinate + * @return The chunk data if it exists, otherwise null + */ + public FluidChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ + return fluidCache.getSubChunkDataAtPoint(worldX, worldY, worldZ); + } + + /** + * Gets the chunk data at a given world position + * @param worldPos The world position as a joml vector + * @return The chunk data if it exists, otherwise null + */ + public FluidChunkData getChunkDataAtWorldPoint(Vector3i worldPos){ + return fluidCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z); + } + + + + /** + * Queues a fluid chunk to be pushed to GPU based on chunk data + * @param data The chunk data (triangles, normals, etc) + * @return The model path that is promised to eventually reflect the fluid model when it makes it to gpu + */ + public static String queueFluidGridGeneration(FluidChunkModelData data){ + String promisedHash = ""; + UUID newUUID = UUID.randomUUID(); + promisedHash = newUUID.toString(); + FluidChunkGenQueueItem queueItem = new FluidChunkGenQueueItem(data, promisedHash); + fluidChunkGenerationQueue.add(queueItem); + return promisedHash; + } + + /** + * Pushes all fluid data in queue to the gpu and registers the resulting models + */ + public static void generateFluidChunkGeometry(){ + for(FluidChunkGenQueueItem queueItem : fluidChunkGenerationQueue){ + Model fluidModel = FluidChunkModelGeneration.generateFluidModel(queueItem.getData()); + Globals.assetManager.registerModelToSpecificString(fluidModel, queueItem.getPromisedHash()); + } + fluidChunkGenerationQueue.clear(); + } + +} diff --git a/src/main/java/electrosphere/client/fluid/manager/FluidChunkGenQueueItem.java b/src/main/java/electrosphere/client/fluid/manager/FluidChunkGenQueueItem.java new file mode 100644 index 00000000..ea1b3d73 --- /dev/null +++ b/src/main/java/electrosphere/client/fluid/manager/FluidChunkGenQueueItem.java @@ -0,0 +1,23 @@ +package electrosphere.client.fluid.manager; + +import electrosphere.entity.types.fluid.FluidChunkModelData; + +public class FluidChunkGenQueueItem { + + FluidChunkModelData data; + String promisedHash; + + public FluidChunkGenQueueItem(FluidChunkModelData data, String promisedHash){ + this.data = data; + this.promisedHash = promisedHash; + } + + public FluidChunkModelData getData(){ + return data; + } + + public String getPromisedHash(){ + return this.promisedHash; + } + +} diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 877cd16d..4e41c798 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -12,6 +12,7 @@ import org.lwjgl.glfw.GLFWErrorCallback; import electrosphere.audio.AudioEngine; import electrosphere.auth.AuthenticationManager; import electrosphere.client.culling.ClientEntityCullingManager; +import electrosphere.client.fluid.manager.ClientFluidManager; import electrosphere.client.foliagemanager.ClientFoliageManager; import electrosphere.client.player.ClientPlayerData; import electrosphere.client.scene.ClientSceneWrapper; @@ -273,6 +274,9 @@ public class Globals { //client terrain manager public static ClientTerrainManager clientTerrainManager; + + //client fluid manager + public static ClientFluidManager clientFluidManager; //client player data public static ClientPlayerData clientPlayerData = new ClientPlayerData(); @@ -439,8 +443,11 @@ public class Globals { //init default shaderProgram defaultMeshShader = ShaderProgram.smart_assemble_shader(false,true); //init terrain shader program - terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain/terrain.vs", "/Shaders/terrain/terrain.fs"); + terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain.vs", "/Shaders/terrain2/terrain.fs"); TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs"); + //init fluid shader program + terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs"); + TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid1/fluid1.vs", "/Shaders/fluid1/fluid1.fs"); //init skybox assetManager.registerModelToSpecificString(RenderUtils.createSkyboxModel(null), AssetDataStrings.ASSET_STRING_SKYBOX_BASIC); //init leaves diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index 4cdac42e..dd3eb54a 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -29,6 +29,11 @@ public class EntityDataStrings { Terrain Entity */ public static final String TERRAIN_IS_TERRAIN = "terrainEntity"; + + /* + * Fluid Entity + */ + public static final String FLUID_IS_FLUID = "fluidEntity"; /* diff --git a/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java b/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java new file mode 100644 index 00000000..389ea07b --- /dev/null +++ b/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java @@ -0,0 +1,36 @@ +package electrosphere.entity.types.fluid; + +import electrosphere.client.fluid.manager.ClientFluidManager; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityCreationUtils; +import electrosphere.entity.EntityDataStrings; +import electrosphere.renderer.meshgen.FluidChunkModelGeneration; + +/** + * Creates a fluid chunk entity + */ +public class FluidChunk { + + /** + * Creates a client fluid chunk based on weights and values provided + * @param weights The fluid weights + * @param values The values (block types) + * @return The fluid chunk entity + */ + public static Entity clientCreateFluidChunkEntity(float[][][] weights, int[][][] values){ + + FluidChunkModelData data = FluidChunkModelGeneration.generateFluidChunkData(weights, values); + String modelPath = ClientFluidManager.queueFluidGridGeneration(data); + + Entity rVal = EntityCreationUtils.createClientSpatialEntity(); + EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); + // if(data.vertices.size() > 0 && levelOfDetail < 1){ + // PhysicsUtils.clientAttachTerrainChunkRigidBody(rVal, data); + // } + + rVal.putData(EntityDataStrings.FLUID_IS_FLUID, true); + + return rVal; + } + +} diff --git a/src/main/java/electrosphere/entity/types/fluid/FluidChunkModelData.java b/src/main/java/electrosphere/entity/types/fluid/FluidChunkModelData.java new file mode 100644 index 00000000..e22b1235 --- /dev/null +++ b/src/main/java/electrosphere/entity/types/fluid/FluidChunkModelData.java @@ -0,0 +1,35 @@ +package electrosphere.entity.types.fluid; + +import java.util.List; + +public class FluidChunkModelData { + + List vertices; + List normals; + List faceElements; + List uvs; + + public FluidChunkModelData(List vertices, List normals, List faceElements, List uvs){ + this.vertices = vertices; + this.normals = normals; + this.faceElements = faceElements; + this.uvs = uvs; + } + + public List getVertices(){ + return vertices; + } + + public List getNormals(){ + return normals; + } + + public List getFaceElements(){ + return faceElements; + } + + public List getUVs(){ + return uvs; + } + +} diff --git a/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java new file mode 100644 index 00000000..05851ed4 --- /dev/null +++ b/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java @@ -0,0 +1,831 @@ +package electrosphere.renderer.meshgen; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.joml.Vector3f; +import org.lwjgl.BufferUtils; + +import electrosphere.engine.Globals; +import electrosphere.entity.types.fluid.FluidChunkModelData; +import electrosphere.renderer.Material; +import electrosphere.renderer.Mesh; +import electrosphere.renderer.Model; +import electrosphere.renderer.ShaderProgram; + +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; + +public class FluidChunkModelGeneration { + + //http://paulbourke.net/geometry/polygonise/ + + static int edgeTable[]={ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + + //256 by 16 + static int triTable[][] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + + private static final byte[] BLOCK_PICK_BITS_BY_VERT_INDEX = new byte[]{ + (byte)0x67, + (byte)0x26, + (byte)0x23, + (byte)0x37, + (byte)0x45, + (byte)0x04, + (byte)0x01, + (byte)0x15, + (byte)0x57, + (byte)0x46, + (byte)0x02, + (byte)0x13 + }; + + static class Triangle { + int[] indices = new int[3]; //array of size 3 + + public Triangle(int index0, int index1, int index2){ + indices[0] = index0; + indices[1] = index1; + indices[2] = index2; + } + } + + static class GridCell { + Vector3f[] points = new Vector3f[8]; //array of size 8 + double[] val = new double[8]; //array of size 8 + public void setValues( + Vector3f p1, Vector3f p2, Vector3f p3, Vector3f p4, + Vector3f p5, Vector3f p6, Vector3f p7, Vector3f p8, + double val1, double val2, double val3, double val4, + double val5, double val6, double val7, double val8 + ){ + points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4; + points[4] = p5; points[5] = p6; points[6] = p7; points[7] = p8; + val[0] = val1; val[1] = val2; val[2] = val3; val[3] = val4; + val[4] = val5; val[5] = val6; val[6] = val7; val[7] = val8; + } + } + + + + + + + + public static ShaderProgram fluidChunkShaderProgram = null; + + + + + + + + + + + + + + protected static int polygonize( + GridCell grid, + double isolevel, + List triangles, + Map vertMap, + List verts, + List normals, + List trianglesSharingVert + ){ + int i; + int ntriang; + int cubeIndex = 0; + Vector3f[] vertList = new Vector3f[12]; + + //get lookup key (index) for edge table + //edge table tells us which vertices are inside of the surface + if (grid.val[0] < isolevel) cubeIndex |= 1; + if (grid.val[1] < isolevel) cubeIndex |= 2; + if (grid.val[2] < isolevel) cubeIndex |= 4; + if (grid.val[3] < isolevel) cubeIndex |= 8; + if (grid.val[4] < isolevel) cubeIndex |= 16; + if (grid.val[5] < isolevel) cubeIndex |= 32; + if (grid.val[6] < isolevel) cubeIndex |= 64; + if (grid.val[7] < isolevel) cubeIndex |= 128; + + //Cube is entirely in/out of the surface + if (edgeTable[cubeIndex] == 0) + return(0); + + //instead of having all intersections be perfectly at the midpoint, + //for each edge this code calculates where along the edge to place the vertex + //this should dramatically smooth the surface + if ((edgeTable[cubeIndex] & 1) > 0) + vertList[0] = + VertexInterp(isolevel,grid.points[0],grid.points[1],grid.val[0],grid.val[1]); + if ((edgeTable[cubeIndex] & 2) > 0) + vertList[1] = + VertexInterp(isolevel,grid.points[1],grid.points[2],grid.val[1],grid.val[2]); + if ((edgeTable[cubeIndex] & 4) > 0) + vertList[2] = + VertexInterp(isolevel,grid.points[2],grid.points[3],grid.val[2],grid.val[3]); + if ((edgeTable[cubeIndex] & 8) > 0) + vertList[3] = + VertexInterp(isolevel,grid.points[3],grid.points[0],grid.val[3],grid.val[0]); + if ((edgeTable[cubeIndex] & 16) > 0) + vertList[4] = + VertexInterp(isolevel,grid.points[4],grid.points[5],grid.val[4],grid.val[5]); + if ((edgeTable[cubeIndex] & 32) > 0) + vertList[5] = + VertexInterp(isolevel,grid.points[5],grid.points[6],grid.val[5],grid.val[6]); + if ((edgeTable[cubeIndex] & 64) > 0) + vertList[6] = + VertexInterp(isolevel,grid.points[6],grid.points[7],grid.val[6],grid.val[7]); + if ((edgeTable[cubeIndex] & 128) > 0) + vertList[7] = + VertexInterp(isolevel,grid.points[7],grid.points[4],grid.val[7],grid.val[4]); + if ((edgeTable[cubeIndex] & 256) > 0) + vertList[8] = + VertexInterp(isolevel,grid.points[0],grid.points[4],grid.val[0],grid.val[4]); + if ((edgeTable[cubeIndex] & 512) > 0) + vertList[9] = + VertexInterp(isolevel,grid.points[1],grid.points[5],grid.val[1],grid.val[5]); + if ((edgeTable[cubeIndex] & 1024) > 0) + vertList[10] = + VertexInterp(isolevel,grid.points[2],grid.points[6],grid.val[2],grid.val[6]); + if ((edgeTable[cubeIndex] & 2048) > 0) + vertList[11] = + VertexInterp(isolevel,grid.points[3],grid.points[7],grid.val[3],grid.val[7]); + + //Create the triangle + ntriang = 0; + for (i=0; triTable[cubeIndex][i]!=-1; i+=3) { + // + // Triangles calculation + // + //get indices + Vector3f vert0 = vertList[triTable[cubeIndex][i+0]]; + Vector3f vert1 = vertList[triTable[cubeIndex][i+1]]; + Vector3f vert2 = vertList[triTable[cubeIndex][i+2]]; + int index0 = getVertIndex(vert0,vertMap,verts); + int index1 = getVertIndex(vert1,vertMap,verts); + int index2 = getVertIndex(vert2,vertMap,verts); + + //add 0's to normals until it matches vert count + while(trianglesSharingVert.size() < verts.size()){ + trianglesSharingVert.add(0); + normals.add(new Vector3f()); + } + + + //add new triangle + Triangle newTriangle = new Triangle(index0,index1,index2); + triangles.add(newTriangle); + ntriang++; + + + + // + // Normals calculation + // + + + //calculate normal for new triangle + Vector3f u = verts.get(index1).sub(verts.get(index0), new Vector3f()); + Vector3f v = verts.get(index2).sub(verts.get(index1), new Vector3f()); + Vector3f n = new Vector3f(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x).normalize(); + + + + //for each vertex, average the new normal with the normals that are already there + int trianglesSharingIndex0 = trianglesSharingVert.get(index0); + //calculate proportion of each normal + float oldProportion = trianglesSharingIndex0 / (float)(trianglesSharingIndex0 + 1); + float newProportion = 1.0f / (float)(trianglesSharingIndex0 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index0, trianglesSharingIndex0 + 1); + + Vector3f currentNormal = normals.get(index0); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index0).set(currentNormal); + + + + + + int trianglesSharingIndex1 = trianglesSharingVert.get(index1); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex1 / (float)(trianglesSharingIndex1 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex1 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index1, trianglesSharingIndex1 + 1); + + currentNormal = normals.get(index1); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index1).set(currentNormal); + + + + + + + + + int trianglesSharingIndex2 = trianglesSharingVert.get(index2); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex2 / (float)(trianglesSharingIndex2 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex2 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index2, trianglesSharingIndex2 + 1); + + currentNormal = normals.get(index2); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index2).set(currentNormal); + + } + + return(ntriang); + } + + //interpolates the location that the edge gets cut based on the magnitudes of the scalars of the vertices at either end of the edge + static Vector3f VertexInterp(double isolevel, Vector3f p1, Vector3f p2, double valp1, double valp2){ + double mu; + float x, y, z; + + if (Math.abs(isolevel-valp1) < 0.00001) + return(p1); + if (Math.abs(isolevel-valp2) < 0.00001) + return(p2); + if (Math.abs(valp1-valp2) < 0.00001) + return(p1); + mu = (isolevel - valp1) / (valp2 - valp1); + x = (float)(p1.x + mu * (p2.x - p1.x)); + y = (float)(p1.y + mu * (p2.y - p1.y)); + z = (float)(p1.z + mu * (p2.z - p1.z)); + + return new Vector3f(x,y,z); + } + + public static FluidChunkModelData generateFluidChunkData(float[][][] weightGrid, int[][][] typeGrid){ + + // 5 6 + // +-------------+ +-----5-------+ ^ Y + // / | / | / | /| | _ + // / | / | 4 9 6 10 | /\ Z + // 4 +-----+-------+ 7 | +-----+7------+ | | / + // | 1 +-------+-----+ 2 | +-----1-+-----+ | / + // | / | / 8 0 11 2 | / + // | / | / | / | / |/ + // 0 +-------------+ 3 +------3------+ +---------------> X + + //the current grid cell + GridCell currentCell = new GridCell(); + //the list of all triangles + List triangles = new LinkedList(); + //the map of vertex to index + Map vertMap = new HashMap(); + //the list of all verts + List verts = new LinkedList(); + //the list of all normals + List normals = new LinkedList(); + //the list of number of triangles that share a vert + List trianglesSharingVert = new LinkedList(); + //List of elements in order + List faceElements = new LinkedList(); + //List of UVs + List UVs = new LinkedList(); + + + + 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++){ + //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), + new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), + weightGrid[x+0][y+0][z+0], weightGrid[x+0][y+0][z+1], weightGrid[x+1][y+0][z+1], weightGrid[x+1][y+0][z+0], + weightGrid[x+0][y+1][z+0], weightGrid[x+0][y+1][z+1], weightGrid[x+1][y+1][z+1], weightGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0, triangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + //all verts in order, flattened as an array of floats instead of vecs + List vertsFlat = new LinkedList(); + //all normals in order, flattened as an array of floats instead of vecs + List normalsFlat = new LinkedList(); + //all elements of faces in order + List elementsFlat = new LinkedList(); + + //flatten verts + normals + for(Vector3f vert : verts){ + vertsFlat.add(vert.x); + vertsFlat.add(vert.y); + vertsFlat.add(vert.z); + } + + for(Vector3f normal : normals){ + normalsFlat.add(normal.x); + normalsFlat.add(normal.y); + normalsFlat.add(normal.z); + } + + + for(Triangle triangle : triangles){ + elementsFlat.add(triangle.indices[0]); + elementsFlat.add(triangle.indices[1]); + elementsFlat.add(triangle.indices[2]); + } + + float[] temp = new float[3]; + int i = 0; + for(Vector3f normal : normals){ + Vector3f vert = verts.get(i); + + float absX = Math.abs(normal.x); + float absY = Math.abs(normal.y); + float absZ = Math.abs(normal.z); + + float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; + float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; + temp[0] = uvX; + temp[1] = uvY; + + // if(absX >= absZ && absX >= absY){ + // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); + // } else if(absZ >= absX && absZ >= absY){ + // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); + // } else if(absY >= absX && absY >= absZ){ + // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); + // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); + // } else { + // temp[0] = vert.x / 1.5f + vert.z / 1.5f; + // temp[1] = vert.y / 1.5f + vert.z / 1.5f; + // } + i++; + UVs.add(temp[0]); + UVs.add(temp[1]); + } + + //List vertices, List normals, List faceElements, List uvs + FluidChunkModelData rVal = new FluidChunkModelData(vertsFlat, normalsFlat, elementsFlat, UVs); + return rVal; + } + + /** + * Generates a mesh based on a fluidchunkdata object + * @param data The fluid chunk data object + * @return The mesh + */ + protected static Mesh generateFluidMesh(FluidChunkModelData data){ + + Mesh mesh = new Mesh(); + + + mesh.mesh = null; + + // + // VAO + // + mesh.vertexArrayObject = glGenVertexArrays(); + glBindVertexArray(mesh.vertexArrayObject); + + + + + + + // + //Buffer data to GPU + // + + try { + mesh.vertexCount = data.getVertices().size() / 3; + FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(mesh.vertexCount * 3); + float[] temp = new float[3]; + for(float vertValue : data.getVertices()){ + VertexArrayBufferData.put(vertValue); + } + VertexArrayBufferData.flip(); + mesh.buffer_vertices(VertexArrayBufferData, 3); + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + // + // FACES + // + mesh.faceCount = data.getFaceElements().size() / 3; + mesh.elementCount = data.getFaceElements().size(); + try { + IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(mesh.elementCount); + int[] temp = new int[3]; + for(int element : data.getFaceElements()){ + elementArrayBufferData.put(element); + } + elementArrayBufferData.flip(); + mesh.buffer_faces(elementArrayBufferData); + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + + // + // NORMALS + // + try { + mesh.normalCount = data.getNormals().size() / 3; + FloatBuffer NormalArrayBufferData; + if(mesh.normalCount > 0){ + NormalArrayBufferData = BufferUtils.createFloatBuffer(mesh.normalCount * 3); + float[] temp = new float[3]; + for(float normalValue : data.getNormals()){ + NormalArrayBufferData.put(normalValue); + } + NormalArrayBufferData.flip(); + mesh.buffer_normals(NormalArrayBufferData, 3); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + // + // TEXTURE COORDINATES + // + try { + mesh.textureCoordCount = data.getUVs().size() / 2; + FloatBuffer TextureArrayBufferData; + if(mesh.textureCoordCount > 0){ + TextureArrayBufferData = BufferUtils.createFloatBuffer(mesh.textureCoordCount * 2); + float[] temp = new float[2]; + for(float uvValue : data.getUVs()){ + TextureArrayBufferData.put(uvValue); + } + TextureArrayBufferData.flip(); + mesh.buffer_texture_coords(TextureArrayBufferData, 2); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + + glBindVertexArray(0); + mesh.nodeID = "fluidChunk"; + return mesh; + } + + + /** + * Generates a model based on a fluidchunkdata object + * @param data The fluid chunk data object + * @return The model + */ + public static Model generateFluidModel(FluidChunkModelData data){ + Model rVal = new Model(); + rVal.meshes = new ArrayList(); + Mesh m = generateFluidMesh(data); + + + Material groundMat = new Material(); + groundMat.set_diffuse("/Textures/Ground/Dirt1.png"); + groundMat.set_specular("/Textures/Ground/Dirt1.png"); + Globals.assetManager.addTexturePathtoQueue("/Textures/Ground/Dirt1.png"); + m.setMaterial(groundMat); + + m.setShader(FluidChunkModelGeneration.fluidChunkShaderProgram); + m.parent = rVal; + + rVal.meshes.add(m); + + return rVal; + } + + //TODO: more optimal key creation + private static String getVertKeyFromPoints(float x, float y, float z){ + return x + "_" + y + "_" + z; + } + + private static int getVertIndex(Vector3f vert, Map vertMap, List verts){ + int rVal = -1; + String vertKey = getVertKeyFromPoints(vert.x,vert.y,vert.z); + if(vertMap.containsKey(vertKey)){ + return vertMap.get(vertKey); + } else { + rVal = verts.size(); + verts.add(vert); + vertMap.put(vertKey,rVal); + return rVal; + } + } + + private static Vector3f averageNormals(Vector3f normal0, float proportion0, Vector3f normal1, float proportion1){ + Vector3f rVal = new Vector3f(normal0); + rVal = rVal.mul(proportion0).add(new Vector3f(normal1).mul(proportion1)); + return rVal; + } + +}