From 716ed65f70e8b7064be3562bd8630c01841be57f Mon Sep 17 00:00:00 2001 From: satellite Date: Sun, 4 Apr 2021 20:31:14 -0400 Subject: [PATCH] Attempted performance tweaks (caching, etc) --- .../electrosphere/game/cell/CellManager.java | 22 ++- .../electrosphere/game/cell/DrawCell.java | 8 +- .../game/terrain/TerrainManager.java | 28 ++- src/main/java/electrosphere/main/Main.java | 2 +- .../electrosphere/renderer/ModelUtils.java | 165 ++++++++++++++++++ 5 files changed, 216 insertions(+), 9 deletions(-) diff --git a/src/main/java/electrosphere/game/cell/CellManager.java b/src/main/java/electrosphere/game/cell/CellManager.java index aa756783..083c33f5 100644 --- a/src/main/java/electrosphere/game/cell/CellManager.java +++ b/src/main/java/electrosphere/game/cell/CellManager.java @@ -1,6 +1,7 @@ package electrosphere.game.cell; import electrosphere.game.terrain.TerrainManager; +import electrosphere.renderer.ShaderProgram; import java.util.Arrays; /** @@ -26,11 +27,13 @@ public class CellManager { boolean[][] updateable; + ShaderProgram program; - int drawRadius = 5; + + int drawRadius = 1; int drawStepdownInterval = 2; - int drawStepdownValue = 10; + int drawStepdownValue = 20; public CellManager(TerrainManager terrainManager, float realX, float realY){ this.terrainManager = terrainManager; @@ -48,6 +51,8 @@ public class CellManager { } cellX = transformRealSpaceToCellSpace(realX); cellY = transformRealSpaceToCellSpace(realY); + + program = ShaderProgram.smart_assemble_shader(false, true); } public int getCellX(){ @@ -98,7 +103,8 @@ public class CellManager { terrainManager.getAugmentedChunkWidth(), currentCellX, currentCellY, - terrainManager.getChunkWidth() + terrainManager.getChunkWidth(), + program ); } valid[targetX][targetY] = true; @@ -135,7 +141,10 @@ public class CellManager { currentCellY < terrainManager.getWorldDiscreteSize() ){ int dist = Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); - int stride = 20;//Math.max(100, dist / drawStepdownInterval * drawStepdownValue); + int stride = Math.min(100, Math.max(3, dist / drawStepdownInterval * drawStepdownValue)); + while(terrainManager.getChunkWidth() % stride != 0){ + stride = stride + 1; + } cells[targetX][targetY].generateDrawableEntity(stride); } drawable[targetX][targetY] = true; @@ -170,7 +179,10 @@ public class CellManager { currentCellY < terrainManager.getWorldDiscreteSize() ){ int dist = Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); - int stride = 20;//Math.max(100, dist / drawStepdownInterval * drawStepdownValue); + int stride = Math.min(100, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); + while(terrainManager.getChunkWidth() % stride != 0){ + stride = stride + 1; + } cells[targetX][targetY].generateDrawableEntity(stride); } updateable[targetX][targetY] = false; diff --git a/src/main/java/electrosphere/game/cell/DrawCell.java b/src/main/java/electrosphere/game/cell/DrawCell.java index fbe9adfc..30309013 100644 --- a/src/main/java/electrosphere/game/cell/DrawCell.java +++ b/src/main/java/electrosphere/game/cell/DrawCell.java @@ -5,6 +5,7 @@ import electrosphere.entity.EntityUtil; import electrosphere.main.Globals; import electrosphere.renderer.Model; import electrosphere.renderer.ModelUtils; +import electrosphere.renderer.ShaderProgram; import electrosphere.util.Utilities; import org.joml.Vector3f; @@ -23,12 +24,15 @@ public class DrawCell { Entity modelEntity; - public DrawCell(float[][] drawArray, int drawWidth, int cellX, int cellY, int cellWidth){ + ShaderProgram program; + + public DrawCell(float[][] drawArray, int drawWidth, int cellX, int cellY, int cellWidth, ShaderProgram program){ this.drawArray = drawArray; this.drawWidth = drawWidth; this.cellX = cellX; this.cellY = cellY; this.cellWidth = cellWidth; + this.program = program; } /** @@ -39,7 +43,7 @@ public class DrawCell { if(modelEntity != null){ Globals.entityManager.deregisterEntity(modelEntity); } - Model terrainModel = ModelUtils.createTerrainModel(drawArray, stride); + Model terrainModel = ModelUtils.createTerrainModelPrecomputedShader(drawArray, program, stride); 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 696be140..abc09023 100644 --- a/src/main/java/electrosphere/game/terrain/TerrainManager.java +++ b/src/main/java/electrosphere/game/terrain/TerrainManager.java @@ -8,6 +8,8 @@ import electrosphere.util.Utilities; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; /** * @@ -27,11 +29,21 @@ public class TerrainManager { TerrainModel model; + //Basic idea is we associate string that contains chunk x&y with elevation + //While we incur a penalty with converting ints -> string, think this will + //offset regenerating the array every time we want a new one + int cacheSize = 50; + HashMap elevationMapCache; + ArrayList elevationMapCacheContents; + + public TerrainManager(int worldSizeDiscrete, int verticalInterpolationRatio, int dynamicInterpolationRatio){ this.worldSizeDiscrete = worldSizeDiscrete; this.verticalInterpolationRatio = verticalInterpolationRatio; this.dynamicInterpolationRatio = dynamicInterpolationRatio; + this.elevationMapCache = new HashMap(); + this.elevationMapCacheContents = new ArrayList(); } public void generate(){ @@ -63,7 +75,21 @@ public class TerrainManager { } public float[][] getAugmentedTerrainAtChunk(int x, int y){ - return model.getAugmentedElevationForChunk(x, y); + String targetChunkName = x + "-" + y; + if(elevationMapCache.containsKey(targetChunkName)){ + elevationMapCacheContents.remove(targetChunkName); + elevationMapCacheContents.add(0, targetChunkName); + return elevationMapCache.get(targetChunkName); + } else { + float[][] targetChunk = model.getAugmentedElevationForChunk(x, y); + if(elevationMapCacheContents.size() > cacheSize){ + String oldChunk = elevationMapCacheContents.remove(elevationMapCacheContents.size() - 1); + elevationMapCache.remove(oldChunk); + } + elevationMapCache.put(targetChunkName, targetChunk); + elevationMapCacheContents.add(targetChunkName); + return targetChunk; + } } public float getHeightAtPosition(float x, float y){ diff --git a/src/main/java/electrosphere/main/Main.java b/src/main/java/electrosphere/main/Main.java index 47fe0369..1e8c067f 100644 --- a/src/main/java/electrosphere/main/Main.java +++ b/src/main/java/electrosphere/main/Main.java @@ -364,7 +364,7 @@ public class Main { static void initPlayer(){ Globals.player = new Entity(); - Globals.player.putData("position", new Vector3f(playerStartRealX,1900f,playerStartRealY)); + Globals.player.putData("position", new Vector3f(playerStartRealX,2200f,playerStartRealY)); Globals.cellManager.setCellX(GL_S); } diff --git a/src/main/java/electrosphere/renderer/ModelUtils.java b/src/main/java/electrosphere/renderer/ModelUtils.java index 53857c21..ef838432 100644 --- a/src/main/java/electrosphere/renderer/ModelUtils.java +++ b/src/main/java/electrosphere/renderer/ModelUtils.java @@ -162,6 +162,171 @@ public class ModelUtils { return rVal; } + + public static Model createTerrainModelPrecomputedShader(float[][] heightfield, ShaderProgram program, int stride){ + Model rVal = new Model(); + rVal.meshes = new ArrayList(); + Mesh m = new Mesh(); + int width = heightfield.length; + int height = heightfield[0].length; + + int actualWidth = (int)Math.ceil(1.0f * width / (1.0f * stride)); + int actualHeight = (int)Math.ceil(1.0f * height / (1.0f * stride)); + +// System.out.println(actualWidth + " " + actualHeight); + +// System.out.println((actualWidth - 1) * (actualHeight - 1)); + + FloatBuffer vertices; + FloatBuffer normals; + IntBuffer faces; + FloatBuffer texture_coords; + if(stride * actualWidth > width){ + int drawWidth = actualWidth + 1; + int drawHeight = actualHeight + 1; + vertices = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 3); + normals = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 3); + faces = BufferUtils.createIntBuffer((drawWidth - 1) * (drawHeight - 1) * 2 * 3); + texture_coords = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 2); + } else { + vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3); + normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 3); + faces = BufferUtils.createIntBuffer((actualWidth - 1) * (actualHeight - 1) * 2 * 3); + texture_coords = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 2); + } + + for(int x = 0; x < width; x = x + stride){ + for(int y = 0; y < height; y = y + stride){ + //deal with vertex + vertices.put(x); + vertices.put(heightfield[x][y]); + vertices.put(y); + //deal with normal + if(x / stride < actualWidth - 1){ + if(y / stride < actualHeight - 1){ + float hL; + if(x > 0){ + hL = heightfield[x-1][y]; + } else { + hL = heightfield[x][y]; + } + float hR = heightfield[x+1][y]; + float hD = heightfield[x][y+1]; + float hU; + if(y > 0){ + hU = heightfield[x][y-1]; + } else { + hU = heightfield[x][y]; + } + Vector3f Normal = new Vector3f(hL - hR, 2.0f, hD - hU); + Normal.normalize(); + normals.put(Normal.x); + normals.put(Normal.y); + normals.put(Normal.z); + } else { + float hL; + if(x > 0){ + hL = heightfield[x-1][y]; + } else { + hL = heightfield[x][y]; + } + float hR = heightfield[x+1][y]; + float hD = heightfield[x][y]; + float hU = heightfield[x][y-1]; + Vector3f Normal = new Vector3f(hL - hR, 2.0f, hD - hU); + Normal.normalize(); + normals.put(Normal.x); + normals.put(Normal.y); + normals.put(Normal.z); + } + } else { + if(y / stride < actualHeight - 1){ + float hL = heightfield[x-1][y]; + float hR = heightfield[x][y]; + float hD = heightfield[x][y+1]; + float hU; + if(y > 0){ + hU = heightfield[x][y-1]; + } else { + hU = heightfield[x][y]; + } + Vector3f Normal = new Vector3f(hL - hR, 2.0f, hD - hU); + Normal.normalize(); + normals.put(Normal.x); + normals.put(Normal.y); + normals.put(Normal.z); + } else { + float hL = heightfield[x-1][y]; + float hR = heightfield[x][y]; + float hD = heightfield[x][y]; + float hU = heightfield[x][y-1]; + Vector3f Normal = new Vector3f(hL - hR, 2.0f, hD - hU); + Normal.normalize(); + normals.put(Normal.x); + normals.put(Normal.y); + normals.put(Normal.z); + } + } + //deal with texture coordinates + if(x / stride % 2 == 0){ + if(y / stride % 2 == 0){ + texture_coords.put(0); + texture_coords.put(0); + } else { + texture_coords.put(0); + texture_coords.put(1); + } + } else { + if(y / stride % 2 == 0){ + texture_coords.put(1); + texture_coords.put(0); + } else { + texture_coords.put(1); + texture_coords.put(1); + } + } + //deal with faces + if(1.0f * x / stride < actualWidth - 1 && 1.0f * y / stride < actualHeight - 1){ + faces.put((x / stride + 0) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 0) * actualHeight + (y / stride + 1)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 0) * actualHeight + (y / stride + 1)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 1)); + } + } + } + + vertices.flip(); + normals.flip(); + faces.flip(); + texture_coords.flip(); + + m.vertexArrayObject = glGenVertexArrays(); + glBindVertexArray(m.vertexArrayObject); + //buffer vertices + m.buffer_vertices(vertices); + //buffer normals + m.buffer_normals(normals); + //buffer faces + m.buffer_faces(faces); + //buffer texture coords + m.buffer_texture_coords(texture_coords); + m.shader = program; + glBindVertexArray(0); + m.parent = rVal; + + Material groundMat = new Material(); + Texture groundTex = new Texture("Textures/Ground/Dirt1.png"); + groundMat.set_diffuse(groundTex); + groundMat.set_specular(groundTex); + m.set_material(groundMat); + + rVal.meshes.add(m); + return rVal; + } + + public static Model createUnitCube(){ Model rVal = new Model(); rVal.meshes = new ArrayList();