Terrain chunking wip

This commit is contained in:
satellite 2021-04-02 19:51:48 -04:00
parent 34c5776296
commit 5d4e5bc775
8 changed files with 280 additions and 48 deletions

View File

@ -0,0 +1,42 @@
package electrosphere.entity;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @author satellite
*/
public class EntityManager {
static CopyOnWriteArrayList<Entity> entityList;
static CopyOnWriteArrayList<Entity> 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<Entity> 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);
}
}
}

View File

@ -38,8 +38,12 @@ public class EntityUtil {
rVal.putData("position", new Vector3f(0,0,0)); rVal.putData("position", new Vector3f(0,0,0));
rVal.putData("rotation", new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0))); rVal.putData("rotation", new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0)));
rVal.putData("scale", new Vector3f(1,1,1)); rVal.putData("scale", new Vector3f(1,1,1));
Globals.entityList.add(rVal); Globals.entityManager.registerEntity(rVal);
Globals.drawableList.add(rVal); Globals.entityManager.registerDrawableEntity(rVal);
return rVal; return rVal;
} }
public static void cleanUpDrawableEntity(Entity e){
getEntityModel(e).free();
}
} }

View File

@ -1,20 +1,163 @@
package electrosphere.game.cell; package electrosphere.game.cell;
import electrosphere.game.terrain.TerrainManager;
import java.util.Arrays;
/** /**
* *
* @author satellite * @author satellite
*/ */
public class CellManager { 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; DrawCell[][] cells;
boolean[][] valid;
boolean[][] drawable;
int drawRadius = 5; int drawRadius = 5;
int drawStepdownInterval = 2; int drawStepdownInterval = 2;
int drawStepdownValue = 10; 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]; 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();
}
} }

View File

@ -2,8 +2,10 @@ package electrosphere.game.cell;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtil; import electrosphere.entity.EntityUtil;
import electrosphere.main.Globals;
import electrosphere.renderer.Model; import electrosphere.renderer.Model;
import electrosphere.util.Utilities; import electrosphere.util.Utilities;
import org.joml.Vector3f;
/** /**
* *
@ -12,19 +14,31 @@ import electrosphere.util.Utilities;
public class DrawCell { public class DrawCell {
float[][] elevation; 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.elevation = elevation;
this.cellX = cellX;
this.cellY = cellY;
this.cellWidth = cellWidth;
} }
/** /**
* Generates a drawable entity based on this chunk * Generates a drawable entity based on this chunk
* @param stride The stride between indices used to generate "sparse" meshes * @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); Model terrainModel = Utilities.create_terrain_model(elevation, stride);
Entity rVal = EntityUtil.spawnDrawableEntity(terrainModel); modelEntity = EntityUtil.spawnDrawableEntity(terrainModel);
return rVal; // System.out.println("New cell @ " + cellX * cellWidth + "," + cellY * cellWidth);
EntityUtil.getEntityPosition(modelEntity).set(new Vector3f(cellX * cellWidth, 0, cellY * cellWidth));
} }
} }

View File

@ -15,21 +15,19 @@ import java.nio.file.Files;
*/ */
public class TerrainManager { 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 //The size of the world in discrete units * must be multiple of 200
int worldSizeDiscrete = 2000; int worldSizeDiscrete = 2000;
int dynamicInterpolationRatio = 200; int dynamicInterpolationRatio = 1000;
TerrainModel model; TerrainModel model;
public TerrainManager(int chunkSize, int worldSizeDiscrete){ public TerrainManager(int worldSizeDiscrete, int dynamicInterpolationRatio){
this.chunkSize = chunkSize;
this.worldSizeDiscrete = worldSizeDiscrete; this.worldSizeDiscrete = worldSizeDiscrete;
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
} }
public void generate(){ public void generate(){
@ -61,11 +59,11 @@ public class TerrainManager {
public float getHeightAtPosition(float x, float y){ public float getHeightAtPosition(float x, float y){
//get chunk coordinate space of input x,y //get chunk coordinate space of input x,y
int chunkX = (int)Math.floor(x / chunkSize); int chunkX = (int)Math.floor(x / dynamicInterpolationRatio);
int chunkY = (int)Math.floor(y / chunkSize); int chunkY = (int)Math.floor(y / dynamicInterpolationRatio);
//get local coordinate space of input x,y //get local coordinate space of input x,y
float localX = x - chunkX * chunkSize; float localX = x - chunkX * dynamicInterpolationRatio;
float localY = y - chunkY * chunkSize; float localY = y - chunkY * dynamicInterpolationRatio;
//get chunk elevation map //get chunk elevation map
float[][] chunkElevationMap = getTerrainAtChunk(chunkX,chunkY); float[][] chunkElevationMap = getTerrainAtChunk(chunkX,chunkY);
//floored variants of local values //floored variants of local values
@ -96,5 +94,12 @@ public class TerrainManager {
return rVal; return rVal;
} }
public int getChunkWidth(){
return dynamicInterpolationRatio;
}
public int getWorldDiscreteSize(){
return worldSizeDiscrete;
}
} }

View File

@ -11,6 +11,8 @@ import electrosphere.renderer.texture.TextureMap;
import com.google.gson.Gson; import com.google.gson.Gson;
import electrosphere.cfg.MainConfig; import electrosphere.cfg.MainConfig;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityManager;
import electrosphere.game.cell.CellManager;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -64,8 +66,7 @@ public class Globals {
public static TextureMap textureMapDefault; public static TextureMap textureMapDefault;
public static CopyOnWriteArrayList<Entity> entityList; public static EntityManager entityManager;
public static CopyOnWriteArrayList<Entity> drawableList;
public static Camera cameraVisible; public static Camera cameraVisible;
@ -76,11 +77,7 @@ public class Globals {
//chunk stuff //chunk stuff
//constant for how far in game units you have to move to load chunks //constant for how far in game units you have to move to load chunks
public static CellManager cellManager;
//array of chunks around view
public static boolean[][] loadedChunk;
public static Entity[][] chunks;
//famous fuckin last words, but temporary solution //famous fuckin last words, but temporary solution
@ -88,6 +85,10 @@ public class Globals {
public static ArrayList<Vector3f> skyboxColors; public static ArrayList<Vector3f> skyboxColors;
//player's entity
public static Entity player;
public static void initGlobals(){ public static void initGlobals(){
//create default textures //create default textures
@ -106,9 +107,8 @@ public class Globals {
//also done in one line //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 :) 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: } catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm:
//create entity list //create entity manager
entityList = new CopyOnWriteArrayList(); entityManager = new EntityManager();
drawableList = new CopyOnWriteArrayList();
//create the camera object that generates view matrix //create the camera object that generates view matrix
cameraVisible = new Camera(); cameraVisible = new Camera();
//init game specific variables //init game specific variables

View File

@ -10,6 +10,7 @@ import electrosphere.renderer.ShaderProgram;
import electrosphere.renderer.texture.Texture; import electrosphere.renderer.texture.Texture;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtil; import electrosphere.entity.EntityUtil;
import electrosphere.game.cell.CellManager;
import electrosphere.game.terrain.TerrainManager; import electrosphere.game.terrain.TerrainManager;
import electrosphere.terraingen.TerrainGen; import electrosphere.terraingen.TerrainGen;
import electrosphere.terraingen.models.TerrainModel; import electrosphere.terraingen.models.TerrainModel;
@ -86,6 +87,10 @@ public class Main {
public static boolean PLAYER_UNDER_USER_CONTROL = true; public static boolean PLAYER_UNDER_USER_CONTROL = true;
public static boolean CAMERA_IS_ORBIT = true; public static boolean CAMERA_IS_ORBIT = true;
public static float camera_Orbit_Length = 1.0f; public static float camera_Orbit_Length = 1.0f;
static final int playerStartRealX = 1000;
static final int playerStartRealY = 1000;
// public static Camera cam_Player_Orbit; // public static Camera cam_Player_Orbit;
//Camera angles using theta-phi system //Camera angles using theta-phi system
//Euler angles where theta is applied, then phi //Euler angles where theta is applied, then phi
@ -124,11 +129,7 @@ public class Main {
initSkybox(); initSkybox();
initPlayer();
Entity player = new Entity();
player.putData("position", new Vector3f(0f,6f,0f));
@ -188,22 +189,22 @@ public class Main {
} }
} }
if(glfwGetKey(Globals.window, GLFW_KEY_W) == GLFW_PRESS){ 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){ 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){ 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){ 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){ 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){ 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 // Draw all entities
// //
Iterator<Entity> entity_iterator = Globals.drawableList.iterator(); Iterator<Entity> entity_iterator = Globals.entityManager.getDrawableIterator();
while(entity_iterator.hasNext()){ while(entity_iterator.hasNext()){
Entity currentEntity = entity_iterator.next(); Entity currentEntity = entity_iterator.next();
Model currentModel = EntityUtil.getEntityModel(currentEntity); Model currentModel = EntityUtil.getEntityModel(currentEntity);
currentModel.modelMatrix = new Matrix4f(); 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.rotate(EntityUtil.getEntityRotation(currentEntity));
currentModel.modelMatrix.scale(EntityUtil.getEntityScale(currentEntity)); currentModel.modelMatrix.scale(EntityUtil.getEntityScale(currentEntity));
currentModel.draw(); currentModel.draw();
@ -301,16 +302,39 @@ public class Main {
static void initWorld(){ static void initWorld(){
float[][] elevation; float[][] elevation;
terrainManager = new TerrainManager(1000,2000); terrainManager = new TerrainManager(2000,200);
if(Globals.mainConfig.loadTerrain){ if(Globals.mainConfig.loadTerrain){
terrainManager.load(); terrainManager.load();
} else { } else {
terrainManager.generate(); terrainManager.generate();
terrainManager.save(); terrainManager.save();
} }
elevation = terrainManager.getTerrainAtChunk(10, 10);
Model terrainModel = Utilities.create_terrain_model(elevation,10); //init cell manager
Entity terrainEntity = EntityUtil.spawnDrawableEntity(terrainModel); 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);
} }
} }

View File

@ -85,9 +85,9 @@ public class Utilities {
int actualWidth = width / stride; int actualWidth = width / stride;
int actualHeight = height / stride; int actualHeight = height / stride;
System.out.println(actualWidth + " " + actualHeight); // System.out.println(actualWidth + " " + actualHeight);
//
System.out.println((actualWidth - 1) * (actualHeight - 1)); // System.out.println((actualWidth - 1) * (actualHeight - 1));
FloatBuffer vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3); FloatBuffer vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3);
FloatBuffer normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3); FloatBuffer normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3);