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("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();
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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<Entity> entityList;
public static CopyOnWriteArrayList<Entity> 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<Vector3f> 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

View File

@ -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> entity_iterator = Globals.drawableList.iterator();
Iterator<Entity> 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);
}
}

View File

@ -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);