package game; import game.blocks.Block; import game.blocks.BlockManager; import org.lwjgl.opengl.GL11; public class Chunk { private static final int WATER_LEVEL = 64; // Size of the terrain measured in cubes public Vector3 arraySize; // Size of each cube public Vector3f cubeSize; // Optional translation public Vector3f translation; // The 3d array containing the cubes public int[][][] terrain; public int x; public int z; private int[][] heightData; private int displayList; public boolean released = false; public boolean isChanged = true; public Chunk(int x, int z, Vector3 arraySize, Vector3f cubeSize, Vector3f translation) { this.x = x; this.z = z; this.arraySize = arraySize; this.cubeSize = cubeSize; this.translation = translation; // Create the cube array terrain = new int[arraySize.x][arraySize.y][arraySize.z]; for(int zz = 0; zz < arraySize.z; zz++) { for(int yy = 0; yy < arraySize.y; yy++) { for(int xx = 0; xx < arraySize.x; xx++) { terrain[xx][yy][zz] = -1; } } } } public void generateTerrain(int maxHeight, int minHeight, int seed, float noiseSize, float persistence, int octaves, boolean textures) { // Stores the height of each x, z coordinate heightData = new int[arraySize.x][arraySize.z]; // Make sure maxHeight and minHeight are within bounds of the cube array if(maxHeight > arraySize.y) maxHeight = arraySize.y; if(maxHeight < 0) maxHeight = 0; if(minHeight > arraySize.y) minHeight = arraySize.y; if(minHeight < 0) minHeight = 0; // Randomize the heights using Perlin noise for(int z = 0; z < arraySize.z; z++) { for(int x = 0; x < arraySize.x; x++) { heightData[x][z] = (int) ((PerlinNoise2D.perlin2D(x + (int) translation.x, z + (int) translation.z, arraySize.x, arraySize.z, seed, noiseSize, persistence, octaves) * (maxHeight - minHeight) + minHeight)) + 56; if (heightData[x][z] > Game.CHUNK_HEIGHT - 1) heightData[x][z] = Game.CHUNK_HEIGHT - 1; if (heightData[x][z] < 1) heightData[x][z] = 1; } } // Create the cubes for(int z = 0; z < arraySize.z; z++) { for(int x = 0; x < arraySize.x; x++) { for(int y = heightData[x][z]; y >= 0; y--) { if (heightData[x][z] >= WATER_LEVEL + 1) { if (y == heightData[x][z]) { terrain[x][y][z] = BlockManager.TYPE_GRASS; } else if (y > heightData[x][z] - 3) { terrain[x][y][z] = BlockManager.TYPE_DIRT; } else { terrain[x][y][z] = BlockManager.TYPE_STONE; } } else { if (y == heightData[x][z]) { terrain[x][y][z] = BlockManager.TYPE_SAND; } else if (y > heightData[x][z] - 3) { terrain[x][y][z] = BlockManager.TYPE_SAND; } else { terrain[x][y][z] = BlockManager.TYPE_STONE; } } } if (heightData[x][z] < WATER_LEVEL) { for (int y = WATER_LEVEL; y > heightData[x][z]; y--) { terrain[x][y][z] = BlockManager.TYPE_WATER; } heightData[x][z] = WATER_LEVEL; } } } for(int z = 0; z < arraySize.z; z++) { for(int y = 1; y < arraySize.y; y++) { for (int x = 0; x < arraySize.x; x++) { double level = SimplexNoise.noise( (float) (x + translation.x) / 30f, (float) (y + translation.y) / 30f, (float) (z + translation.z) / 30f); if (level > 0.8d) { if ((terrain[x][y][z] > -1) && (terrain[x][y][z] != Block.TYPE_WATER)) terrain[x][y][z] = -1; } if ((heightData[x][z] - y) > 3) { level = SimplexNoise.noise( (float) (x + translation.x) / 14f, (float) (y + translation.y) / 7f, (float) (z + translation.z) / 14f); if (level > ( (SimplexNoise.noise( ((float) x + translation.x) / 50, ((float) z + translation.z) / 50 ) + 0.7d) ) ) { if ((terrain[x][y][z] > -1) && (terrain[x][y][z] != Block.TYPE_WATER)) terrain[x][y][z] = -1; } } } } } } /* Returns true if there is a solid cube at the given coordinates. */ public boolean solidAt(Vector3f coordinates) { // Get the cube coordinates in the array Vector3 arrayCoordinates = new Vector3((int)((coordinates.x - translation.x) / cubeSize.x), (int)((coordinates.y - translation.y) / cubeSize.y), (int)((coordinates.z - translation.z) / cubeSize.z)); // Is this within the array bounds? if(arrayCoordinates.x >= 0 && arrayCoordinates.x < arraySize.x && arrayCoordinates.y >= 0 && arrayCoordinates.y < arraySize.y && arrayCoordinates.z >= 0 && arrayCoordinates.z < arraySize.z) { // Is there a cube at this coordinate? if((terrain[arrayCoordinates.x][arrayCoordinates.y][arrayCoordinates.z] > -1) && (terrain[arrayCoordinates.x][arrayCoordinates.y][arrayCoordinates.z] != Block.TYPE_WATER)) { return true; } } return false; } public void render() { // Call the display list released = false; if (isChanged) { isChanged = false; release(); released = false; displayList = GL11.glGenLists(1); GL11.glNewList(displayList, GL11.GL_COMPILE); Chunk[] chunkArray = new Chunk[]{this}; for(int z = 0; z < arraySize.z; z++) { for(int x = 0; x < arraySize.x; x++) { for(int y = 0; y < arraySize.y; y++) { if(terrain[x][y][z] > -1) { BlockManager.renderType(chunkArray, terrain[x][y][z], new Vector3(x,y,z)); } } } } QuadQueue.renderAll(); GL11.glEndList(); } if (!released) GL11.glCallList(displayList); } public void release() { GL11.glDeleteLists(displayList, 1); released = true; } public void deleteBlockAt(Vector3f pos) { isChanged = true; Vector3 index = new Vector3( (int) Math.floor(((pos.x % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE), (int) Math.floor(((pos.y % Game.CHUNK_HEIGHT) + Game.CHUNK_HEIGHT) % Game.CHUNK_HEIGHT), (int) Math.floor(((pos.z % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE) ); if ((index.x >= 0) && (index.y >= 0) && (index.z >= 0)) { terrain[index.x][index.y][index.z] = -1; release(); render(); } } public void createBlockAt(Vector3f pos) { isChanged = true; Vector3 index = new Vector3( (int) Math.floor(((pos.x % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE), (int) Math.floor(((pos.y % Game.CHUNK_HEIGHT) + Game.CHUNK_HEIGHT) % Game.CHUNK_HEIGHT), (int) Math.floor(((pos.z % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE) ); if ((index.x >= 0) && (index.y >= 0) && (index.z >= 0)) { terrain[index.x][index.y][index.z] = ItemBox.getSelectedBlock(); // todo change release(); render(); } } public int getCubeTypeAtVector(Vector3f pos) { Vector3 index = new Vector3( (int) Math.floor(((pos.x % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE), (int) Math.floor((pos.y + Game.CHUNK_HEIGHT) % Game.CHUNK_HEIGHT), (int) Math.floor(((pos.z % Game.CHUNK_SIZE) + Game.CHUNK_SIZE) % Game.CHUNK_SIZE) ); return ((index.x >= 0) && (index.y >= 0) && (index.z >= 0) && (terrain[index.x][index.y][index.z] < 0)) ? terrain[index.x][index.y][index.z] : 0; } }