From 5d4e5bc7750c68d291948a3bc4943cec6d36bbc1 Mon Sep 17 00:00:00 2001 From: satellite Date: Fri, 2 Apr 2021 19:51:48 -0400 Subject: [PATCH] Terrain chunking wip --- .../electrosphere/entity/EntityManager.java | 42 +++++ .../java/electrosphere/entity/EntityUtil.java | 8 +- .../electrosphere/game/cell/CellManager.java | 147 +++++++++++++++++- .../electrosphere/game/cell/DrawCell.java | 24 ++- .../game/terrain/TerrainManager.java | 23 +-- src/main/java/electrosphere/main/Globals.java | 20 +-- src/main/java/electrosphere/main/Main.java | 58 +++++-- .../java/electrosphere/util/Utilities.java | 6 +- 8 files changed, 280 insertions(+), 48 deletions(-) create mode 100644 src/main/java/electrosphere/entity/EntityManager.java diff --git a/src/main/java/electrosphere/entity/EntityManager.java b/src/main/java/electrosphere/entity/EntityManager.java new file mode 100644 index 00000000..4e98dff7 --- /dev/null +++ b/src/main/java/electrosphere/entity/EntityManager.java @@ -0,0 +1,42 @@ +package electrosphere.entity; + +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * + * @author satellite + */ +public class EntityManager { + + static CopyOnWriteArrayList entityList; + static CopyOnWriteArrayList drawableList; + + public EntityManager(){ + entityList = new CopyOnWriteArrayList(); + drawableList = new CopyOnWriteArrayList(); + } + + public void registerEntity(Entity e){ + entityList.add(e); + } + + public void registerDrawableEntity(Entity e){ + drawableList.add(e); + } + + public Iterator getDrawableIterator(){ + return drawableList.iterator(); + } + + public void deregisterEntity(Entity e){ + if(drawableList.contains(e)){ + drawableList.remove(e); + EntityUtil.cleanUpDrawableEntity(e); + } + if(entityList.contains(e)){ + entityList.remove(e); + } + } + +} diff --git a/src/main/java/electrosphere/entity/EntityUtil.java b/src/main/java/electrosphere/entity/EntityUtil.java index 51d24591..51868b43 100644 --- a/src/main/java/electrosphere/entity/EntityUtil.java +++ b/src/main/java/electrosphere/entity/EntityUtil.java @@ -38,8 +38,12 @@ public class EntityUtil { rVal.putData("position", new Vector3f(0,0,0)); rVal.putData("rotation", new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0))); rVal.putData("scale", new Vector3f(1,1,1)); - Globals.entityList.add(rVal); - Globals.drawableList.add(rVal); + Globals.entityManager.registerEntity(rVal); + Globals.entityManager.registerDrawableEntity(rVal); return rVal; } + + public static void cleanUpDrawableEntity(Entity e){ + getEntityModel(e).free(); + } } diff --git a/src/main/java/electrosphere/game/cell/CellManager.java b/src/main/java/electrosphere/game/cell/CellManager.java index 1026ac26..2ccdeccc 100644 --- a/src/main/java/electrosphere/game/cell/CellManager.java +++ b/src/main/java/electrosphere/game/cell/CellManager.java @@ -1,20 +1,163 @@ package electrosphere.game.cell; +import electrosphere.game.terrain.TerrainManager; +import java.util.Arrays; + /** * * @author satellite */ public class CellManager { + //the terrain manager this cell manager constructs off of + TerrainManager terrainManager; + + //the center of this cell manager's array in cell space + int cellX; + int cellY; + + //the width of a minicell in this manager + int miniCellWidth; + + //all currently displaying mini cells DrawCell[][] cells; + boolean[][] valid; + boolean[][] drawable; + + + int drawRadius = 5; int drawStepdownInterval = 2; int drawStepdownValue = 10; - public CellManager(){ + public CellManager(TerrainManager terrainManager, float realX, int realY){ + this.terrainManager = terrainManager; + this.miniCellWidth = miniCellWidth; cells = new DrawCell[drawRadius * 2 + 1][drawRadius * 2 + 1]; - + valid = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1]; + drawable = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1]; + for(int x = 0; x < drawRadius * 2 + 1; x++){ + for(int y = 0; y < drawRadius * 2 + 1; y++){ + valid[x][y] = false; + drawable[x][y] = false; + } + } + cellX = transformRealSpaceToCellSpace(realX); + cellY = transformRealSpaceToCellSpace(realY); } + public int getCellX(){ + return cellX; + } + + public int getCellY(){ + return cellY; + } + + public void setCellX(int x){ + cellX = x; + } + + public void setCellY(int y){ + cellY = y; + } + + public void updateInvalidCell(){ + int targetX = 0; + int targetY = 0; + boolean found = false; + for(int x = 0; x < drawRadius * 2 + 1; x++){ + targetX = x; + for(int y = 0; y < drawRadius * 2 + 1; y++){ + targetY = y; + if(!valid[x][y]){ + found = true; + break; + } + } + if(found){ + break; + } + } + + if(found){ + int currentCellX = cellX - drawRadius + targetX; + int currentCellY = cellY - drawRadius + targetY; + if( + currentCellX >= 0 && + currentCellX < terrainManager.getWorldDiscreteSize() && + currentCellY >= 0 && + currentCellY < terrainManager.getWorldDiscreteSize() + ){ + cells[targetX][targetY] = new DrawCell(terrainManager.getTerrainAtChunk(cellX, cellY),cellX,cellY,terrainManager.getChunkWidth()); + } + valid[targetX][targetY] = true; + } + } + + public void makeCellDrawable(){ + int targetX = 0; + int targetY = 0; + boolean found = false; + for(int x = 0; x < drawRadius * 2 + 1; x++){ + targetX = x; + for(int y = 0; y < drawRadius * 2 + 1; y++){ + targetY = y; + if(valid[x][y] && !drawable[x][y]){ + found = true; + break; + } + } + if(found){ + break; + } + } + + if(found){ + int currentCellX = cellX - drawRadius + targetX; + int currentCellY = cellY - drawRadius + targetY; + if( + currentCellX >= 0 && + currentCellX < terrainManager.getWorldDiscreteSize() && + currentCellY >= 0 && + currentCellY < terrainManager.getWorldDiscreteSize() + ){ + int dist = Math.abs(cellX - drawRadius) * Math.abs(cellY - drawRadius); + int stride = Math.max(100, dist / drawStepdownInterval * drawStepdownValue); + cells[targetX][targetY].generateDrawableEntity(stride); + } + drawable[targetX][targetY] = true; + } + } + + + public boolean containsInvalidCell(){ + for(int x = 0;x < drawRadius * 2 + 1; x++){ + for(int y = 0; y < drawRadius * 2 + 1; y++){ + if(!valid[x][y]){ + return true; + } + } + } + return false; + } + + public boolean containsUndrawableCell(){ + for(int x = 0;x < drawRadius * 2 + 1; x++){ + for(int y = 0; y < drawRadius * 2 + 1; y++){ + if(!drawable[x][y]){ + return true; + } + } + } + return false; + } + + + public int transformRealSpaceToCellSpace(float input){ + return (int)input / terrainManager.getChunkWidth(); + } + + } diff --git a/src/main/java/electrosphere/game/cell/DrawCell.java b/src/main/java/electrosphere/game/cell/DrawCell.java index 7fc99c2a..88afc8fa 100644 --- a/src/main/java/electrosphere/game/cell/DrawCell.java +++ b/src/main/java/electrosphere/game/cell/DrawCell.java @@ -2,8 +2,10 @@ package electrosphere.game.cell; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtil; +import electrosphere.main.Globals; import electrosphere.renderer.Model; import electrosphere.util.Utilities; +import org.joml.Vector3f; /** * @@ -12,19 +14,31 @@ import electrosphere.util.Utilities; public class DrawCell { float[][] elevation; - public DrawCell(float[][] elevation){ + int cellX; + int cellY; + int cellWidth; + + Entity modelEntity; + + public DrawCell(float[][] elevation, int cellX, int cellY, int cellWidth){ this.elevation = elevation; + this.cellX = cellX; + this.cellY = cellY; + this.cellWidth = cellWidth; } /** * Generates a drawable entity based on this chunk * @param stride The stride between indices used to generate "sparse" meshes - * @return A drawable entity that contains the terrain mesh of a certain stride */ - public Entity generateDrawableEntity(int stride){ + public void generateDrawableEntity(int stride){ + if(modelEntity != null){ + Globals.entityManager.deregisterEntity(modelEntity); + } Model terrainModel = Utilities.create_terrain_model(elevation, stride); - Entity rVal = EntityUtil.spawnDrawableEntity(terrainModel); - return rVal; + modelEntity = EntityUtil.spawnDrawableEntity(terrainModel); +// System.out.println("New cell @ " + cellX * cellWidth + "," + cellY * cellWidth); + EntityUtil.getEntityPosition(modelEntity).set(new Vector3f(cellX * cellWidth, 0, cellY * cellWidth)); } } diff --git a/src/main/java/electrosphere/game/terrain/TerrainManager.java b/src/main/java/electrosphere/game/terrain/TerrainManager.java index ab694317..edc957e0 100644 --- a/src/main/java/electrosphere/game/terrain/TerrainManager.java +++ b/src/main/java/electrosphere/game/terrain/TerrainManager.java @@ -15,21 +15,19 @@ import java.nio.file.Files; */ public class TerrainManager { - //This is the dimension in x and y of the chunk - int chunkSize = 200; //The size of the world in discrete units * must be multiple of 200 int worldSizeDiscrete = 2000; - int dynamicInterpolationRatio = 200; + int dynamicInterpolationRatio = 1000; TerrainModel model; - public TerrainManager(int chunkSize, int worldSizeDiscrete){ - this.chunkSize = chunkSize; + public TerrainManager(int worldSizeDiscrete, int dynamicInterpolationRatio){ this.worldSizeDiscrete = worldSizeDiscrete; + this.dynamicInterpolationRatio = dynamicInterpolationRatio; } public void generate(){ @@ -61,11 +59,11 @@ public class TerrainManager { public float getHeightAtPosition(float x, float y){ //get chunk coordinate space of input x,y - int chunkX = (int)Math.floor(x / chunkSize); - int chunkY = (int)Math.floor(y / chunkSize); + int chunkX = (int)Math.floor(x / dynamicInterpolationRatio); + int chunkY = (int)Math.floor(y / dynamicInterpolationRatio); //get local coordinate space of input x,y - float localX = x - chunkX * chunkSize; - float localY = y - chunkY * chunkSize; + float localX = x - chunkX * dynamicInterpolationRatio; + float localY = y - chunkY * dynamicInterpolationRatio; //get chunk elevation map float[][] chunkElevationMap = getTerrainAtChunk(chunkX,chunkY); //floored variants of local values @@ -96,5 +94,12 @@ public class TerrainManager { return rVal; } + public int getChunkWidth(){ + return dynamicInterpolationRatio; + } + + public int getWorldDiscreteSize(){ + return worldSizeDiscrete; + } } diff --git a/src/main/java/electrosphere/main/Globals.java b/src/main/java/electrosphere/main/Globals.java index 78f40698..b897deb1 100644 --- a/src/main/java/electrosphere/main/Globals.java +++ b/src/main/java/electrosphere/main/Globals.java @@ -11,6 +11,8 @@ import electrosphere.renderer.texture.TextureMap; import com.google.gson.Gson; import electrosphere.cfg.MainConfig; import electrosphere.entity.Entity; +import electrosphere.entity.EntityManager; +import electrosphere.game.cell.CellManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -64,8 +66,7 @@ public class Globals { public static TextureMap textureMapDefault; - public static CopyOnWriteArrayList entityList; - public static CopyOnWriteArrayList drawableList; + public static EntityManager entityManager; public static Camera cameraVisible; @@ -76,11 +77,7 @@ public class Globals { //chunk stuff //constant for how far in game units you have to move to load chunks - - - //array of chunks around view - public static boolean[][] loadedChunk; - public static Entity[][] chunks; + public static CellManager cellManager; //famous fuckin last words, but temporary solution @@ -88,6 +85,10 @@ public class Globals { public static ArrayList skyboxColors; + //player's entity + public static Entity player; + + public static void initGlobals(){ //create default textures @@ -106,9 +107,8 @@ public class Globals { //also done in one line textureMapDefault = gson.fromJson(Files.newBufferedReader(new File(Thread.currentThread().getContextClassLoader().getResource("Textures/default_texture_map.json").getFile()).toPath()), TextureMap.class); //only the best of coding practices :) } catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm: - //create entity list - entityList = new CopyOnWriteArrayList(); - drawableList = new CopyOnWriteArrayList(); + //create entity manager + entityManager = new EntityManager(); //create the camera object that generates view matrix cameraVisible = new Camera(); //init game specific variables diff --git a/src/main/java/electrosphere/main/Main.java b/src/main/java/electrosphere/main/Main.java index fc50e4b0..9bb208b4 100644 --- a/src/main/java/electrosphere/main/Main.java +++ b/src/main/java/electrosphere/main/Main.java @@ -10,6 +10,7 @@ import electrosphere.renderer.ShaderProgram; import electrosphere.renderer.texture.Texture; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtil; +import electrosphere.game.cell.CellManager; import electrosphere.game.terrain.TerrainManager; import electrosphere.terraingen.TerrainGen; import electrosphere.terraingen.models.TerrainModel; @@ -86,6 +87,10 @@ public class Main { public static boolean PLAYER_UNDER_USER_CONTROL = true; public static boolean CAMERA_IS_ORBIT = true; public static float camera_Orbit_Length = 1.0f; + + static final int playerStartRealX = 1000; + static final int playerStartRealY = 1000; + // public static Camera cam_Player_Orbit; //Camera angles using theta-phi system //Euler angles where theta is applied, then phi @@ -124,11 +129,7 @@ public class Main { initSkybox(); - - - Entity player = new Entity(); - player.putData("position", new Vector3f(0f,6f,0f)); - + initPlayer(); @@ -188,22 +189,22 @@ public class Main { } } if(glfwGetKey(Globals.window, GLFW_KEY_W) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z)); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z)); } if(glfwGetKey(Globals.window, GLFW_KEY_S) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(camera_Current.pos_Center.x,0,camera_Current.pos_Center.z)); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(camera_Current.pos_Center.x,0,camera_Current.pos_Center.z)); } if(glfwGetKey(Globals.window, GLFW_KEY_D) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z).rotateY((float)(-90 * Math.PI / 180))); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z).rotateY((float)(-90 * Math.PI / 180))); } if(glfwGetKey(Globals.window, GLFW_KEY_A) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z).rotateY((float)(90 * Math.PI / 180))); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(-camera_Current.pos_Center.x,0,-camera_Current.pos_Center.z).rotateY((float)(90 * Math.PI / 180))); } if(glfwGetKey(Globals.window, GLFW_KEY_SPACE) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(0,0.6f,0)); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(0,0.6f,0)); } if(glfwGetKey(Globals.window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS){ - EntityUtil.getEntityPosition(player).add(new Vector3f(0,-0.6f,0)); + EntityUtil.getEntityPosition(Globals.player).add(new Vector3f(0,-0.6f,0)); } @@ -222,12 +223,12 @@ public class Main { // // Draw all entities // - Iterator entity_iterator = Globals.drawableList.iterator(); + Iterator entity_iterator = Globals.entityManager.getDrawableIterator(); while(entity_iterator.hasNext()){ Entity currentEntity = entity_iterator.next(); Model currentModel = EntityUtil.getEntityModel(currentEntity); currentModel.modelMatrix = new Matrix4f(); - currentModel.modelMatrix.translate(new Vector3f(EntityUtil.getEntityPosition(currentEntity)).sub(EntityUtil.getEntityPosition(player))); + currentModel.modelMatrix.translate(new Vector3f(EntityUtil.getEntityPosition(currentEntity)).sub(EntityUtil.getEntityPosition(Globals.player))); currentModel.modelMatrix.rotate(EntityUtil.getEntityRotation(currentEntity)); currentModel.modelMatrix.scale(EntityUtil.getEntityScale(currentEntity)); currentModel.draw(); @@ -301,16 +302,39 @@ public class Main { static void initWorld(){ + float[][] elevation; - terrainManager = new TerrainManager(1000,2000); + terrainManager = new TerrainManager(2000,200); if(Globals.mainConfig.loadTerrain){ terrainManager.load(); } else { terrainManager.generate(); terrainManager.save(); } - elevation = terrainManager.getTerrainAtChunk(10, 10); - Model terrainModel = Utilities.create_terrain_model(elevation,10); - Entity terrainEntity = EntityUtil.spawnDrawableEntity(terrainModel); + + //init cell manager + int cellX = 0; + int cellY = 0; + Globals.cellManager = new CellManager(terrainManager, playerStartRealX, playerStartRealY); + + + while(Globals.cellManager.containsInvalidCell()){ + Globals.cellManager.updateInvalidCell(); + } + + while(Globals.cellManager.containsUndrawableCell()){ + Globals.cellManager.makeCellDrawable(); + } + +// elevation = terrainManager.getTerrainAtChunk(10, 10); +// Model terrainModel = Utilities.create_terrain_model(elevation,10); +// Entity terrainEntity = EntityUtil.spawnDrawableEntity(terrainModel); + } + + static void initPlayer(){ + Globals.player = new Entity(); + Globals.player.putData("position", new Vector3f(playerStartRealX,6f,playerStartRealY)); + + Globals.cellManager.setCellX(GL_S); } } diff --git a/src/main/java/electrosphere/util/Utilities.java b/src/main/java/electrosphere/util/Utilities.java index 20ae368d..ed6e9f32 100644 --- a/src/main/java/electrosphere/util/Utilities.java +++ b/src/main/java/electrosphere/util/Utilities.java @@ -85,9 +85,9 @@ public class Utilities { int actualWidth = width / stride; int actualHeight = height / stride; - System.out.println(actualWidth + " " + actualHeight); - - System.out.println((actualWidth - 1) * (actualHeight - 1)); +// System.out.println(actualWidth + " " + actualHeight); +// +// System.out.println((actualWidth - 1) * (actualHeight - 1)); FloatBuffer vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3); FloatBuffer normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3);