package electrosphere.game.client.cells; import electrosphere.game.client.terrain.manager.ClientTerrainManager; import electrosphere.game.server.terrain.manager.ServerTerrainManager; import electrosphere.game.client.world.ClientWorldData; import electrosphere.main.Globals; import electrosphere.net.parser.net.message.WorldMessage; import electrosphere.renderer.ShaderProgram; import java.util.Arrays; import java.util.HashMap; import org.joml.Vector3f; /** * * @author satellite */ public class DrawCellManager { //the center of this cell manager's array in cell space int cellX; int cellY; //the dimensions of the world that this cell manager can handles int cellWidth; int cellHeight; //the width of a minicell in this manager int miniCellWidth; //all currently displaying mini cells DrawCell[][] cells; boolean[][] valid; boolean[][] drawable; boolean[][] updateable; boolean[][] hasRequested; boolean[][] needsPhysics; boolean[][] hasPhysics; ShaderProgram program; int drawRadius = 5; int drawStepdownInterval = 3; int drawStepdownValue = 25; int physicsRadius = 3; int worldBoundDiscreteMin = 0; int worldBoundDiscreteMax = 0; //metadata about the game world ClientWorldData clientWorldData; //client terrain manager ClientTerrainManager clientTerrainManager; //ready to start updating? boolean update = false; public DrawCellManager(ClientWorldData clientWorldData, ClientTerrainManager clientTerrainManager, int discreteX, int discreteY){ this.clientWorldData = clientWorldData; worldBoundDiscreteMax = (int)(this.clientWorldData.getWorldBoundMax().x / this.clientWorldData.getDynamicInterpolationRatio() * 1.0f); this.clientTerrainManager = clientTerrainManager; 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]; updateable = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1]; hasRequested = new boolean[drawRadius * 2 + 1][drawRadius * 2 + 1]; needsPhysics = new boolean[physicsRadius * 2 + 1][physicsRadius * 2 + 1]; hasPhysics = new boolean[physicsRadius * 2 + 1][physicsRadius * 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; updateable[x][y] = false; hasRequested[x][y] = false; } } for(int x = 0; x < physicsRadius * 2 + 1; x++){ for(int y = 0; y < physicsRadius * 2 + 1; y++){ needsPhysics[x][y] = true; hasPhysics[x][y] = false; } } cellX = discreteX; cellY = discreteY; program = Globals.defaultMeshShader; drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius(); drawStepdownInterval = Globals.userSettings.getGameplayPhysicsCellRadius(); physicsRadius = Globals.userSettings.getGameplayPhysicsCellRadius(); update = true; } DrawCellManager(){ } public int getCellX(){ return cellX; } public int getCellY(){ return cellY; } public void setCellX(int x){ cellX = x; } public void setCellY(int y){ cellY = y; } 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 < clientWorldData.getWorldDiscreteSize() && currentCellY >= 0 && currentCellY < clientWorldData.getWorldDiscreteSize() ){ if(clientTerrainManager.containsHeightmapAtDiscretePoint(currentCellX, currentCellY)){ cells[targetX][targetY] = DrawCell.generateTerrainCell( currentCellX, currentCellY, clientTerrainManager.getHeightmapAtPoint(currentCellX, currentCellY), clientWorldData.getDynamicInterpolationRatio(), program ); valid[targetX][targetY] = true; drawable[targetX][targetY] = false; updateable[targetX][targetY] = false; hasRequested[targetX][targetY] = false; // if(Math.abs(physicsRadius + 1 - targetX) < physicsRadius && Math.abs(physicsRadius + 1 - targetY) < physicsRadius){ // needsPhysics[targetX][targetY] = true; // } } else { if(hasRequested[targetX][targetY] == false){ //client should request macro values from server Globals.clientConnection.queueOutgoingMessage(WorldMessage.constructRequestChunkMessage(currentCellX, currentCellY)); hasRequested[targetX][targetY] = true; } } } else { valid[targetX][targetY] = true; drawable[targetX][targetY] = false; updateable[targetX][targetY] = false; hasRequested[targetX][targetY] = false; } } } 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 < clientWorldData.getWorldDiscreteSize() && currentCellY >= 0 && currentCellY < clientWorldData.getWorldDiscreteSize() ){ //physics radius calculation // if(Math.abs(physicsRadius + 1 - targetX) < physicsRadius && Math.abs(physicsRadius + 1 - targetY) < physicsRadius){ // needsPhysics[targetX][targetY] = true; // } //calculation for stride int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius));//Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); int stride = Math.min(clientWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); while(clientWorldData.getDynamicInterpolationRatio() % stride != 0){ stride = stride + 1; } //make drawable entity cells[targetX][targetY].generateDrawableEntity(stride); } drawable[targetX][targetY] = true; } } void updateCellModel(){ 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(updateable[x][y]){ found = true; break; } } if(found){ break; } } if(found){ int currentCellX = cellX - drawRadius + targetX; int currentCellY = cellY - drawRadius + targetY; if( currentCellX >= 0 && currentCellX < clientWorldData.getWorldDiscreteSize() && currentCellY >= 0 && currentCellY < clientWorldData.getWorldDiscreteSize() ){ // if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){ // needsPhysics[targetX][targetY] = true; // } int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); int stride = Math.min(clientWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); while(clientWorldData.getDynamicInterpolationRatio() % stride != 0){ stride = stride + 1; } cells[targetX][targetY].generateDrawableEntity(stride); } updateable[targetX][targetY] = false; drawable[targetX][targetY] = true; } } public boolean containsInvalidCell(){ // if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){ for(int x = 0;x < drawRadius * 2 + 1; x++){ for(int y = 0; y < drawRadius * 2 + 1; y++){ if(!valid[x][y]){ return true; } } } // } else if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){ // return cells[0][0] == null || cells[1][0] == null || cells[0][1] == null | cells[1][1] == null; // } 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 boolean containsUpdateableCell(){ for(int x = 0;x < drawRadius * 2 + 1; x++){ for(int y = 0; y < drawRadius * 2 + 1; y++){ if(updateable[x][y]){ return true; } } } return false; } public boolean containsPhysicsNeedingCell(){ for(int x = 0; x < physicsRadius * 2 + 1; x++){ for(int y = 0; y < physicsRadius * 2 + 1; y++){ if(needsPhysics[x][y]){ return true; } } } return false; } public void addPhysicsToCell(){ int targetX = 0; int targetY = 0; boolean found = false; for(int x = 0; x < physicsRadius * 2 + 1; x++){ targetX = x; for(int y = 0; y < physicsRadius * 2 + 1; y++){ targetY = y; // System.out.println(x + " <=>w " + y); if(needsPhysics[x][y]){ found = true; break; } } if(found){ break; } } if(found){ int currentCellX = cellX - physicsRadius + targetX; int currentCellY = cellY - physicsRadius + targetY; if( currentCellX >= 0 && currentCellX < clientWorldData.getWorldDiscreteSize() && currentCellY >= 0 && currentCellY < clientWorldData.getWorldDiscreteSize() ){ // if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){ // needsPhysics[targetX][targetY] = true; // } // int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); // int stride = Math.min(clientWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); // while(clientWorldData.getDynamicInterpolationRatio() % stride != 0){ // stride = stride + 1; // } // if(cells[targetX][targetY + drawRadius] != null){ // System.out.println(targetX + " - " + targetY); cells[targetX + drawRadius - physicsRadius][targetY + drawRadius - physicsRadius].generatePhysics(); // } else { // System.out.println("Current cell is null: " + currentCellX + " - " + currentCellY); // } } needsPhysics[targetX][targetY] = false; hasPhysics[targetX][targetY] = true; } } public void shiftChunksNegX(){ //retire old graphics for(int y = 0; y < drawRadius * 2 + 1; y++){ if(cells[drawRadius * 2][y] != null){ cells[drawRadius * 2][y].retireCell(); } } //shift draw array for(int x = drawRadius * 2; x > 0; x--){ for(int y = 0; y < drawRadius * 2 + 1; y++){ cells[x][y] = cells[x-1][y]; updateable[x][y] = true; hasRequested[x][y] = hasRequested[x-1][y]; } } //invalidate edge of draw array for(int y = 0; y < drawRadius * 2 + 1; y++){ valid[0][y] = false; hasRequested[0][y] = false; } //retire physics of cells for(int x = 0; x < physicsRadius * 2 + 1; x++){ if(hasPhysics[x][physicsRadius * 2]){ cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics(); } } //shift physics array for(int x = physicsRadius * 2; x > 0; x--){ for(int y = 0; y < physicsRadius * 2 + 1; y++){ needsPhysics[x][y] = needsPhysics[x-1][y]; hasPhysics[x][y] = hasPhysics[x-1][y]; } } //invalidate edge of physics array for(int y = 0; y < physicsRadius * 2 + 1; y++){ needsPhysics[0][y] = true; hasPhysics[0][y] = false; } } public void shiftChunksPosX(){ //retire old graphics for(int y = 0; y < drawRadius * 2 + 1; y++){ if(cells[0][y] != null){ cells[0][y].retireCell(); } } //shift draw array for(int x = 0; x < drawRadius * 2; x++){ for(int y = 0; y < drawRadius * 2 + 1; y++){ cells[x][y] = cells[x+1][y]; updateable[x][y] = true; hasRequested[x][y] = hasRequested[x+1][y]; } } //invalidate edge of draw array for(int y = 0; y < drawRadius * 2 + 1; y++){ valid[drawRadius * 2][y] = false; hasRequested[drawRadius * 2][y] = false; } //retire physics of cells for(int x = 0; x < physicsRadius * 2 + 1; x++){ if(hasPhysics[x][physicsRadius * 2]){ cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics(); } } //shift physics array for(int x = 0; x < physicsRadius * 2; x++){ for(int y = 0; y < physicsRadius * 2 + 1; y++){ needsPhysics[x][y] = needsPhysics[x+1][y]; hasPhysics[x][y] = hasPhysics[x+1][y]; } } //invalidate edge of physics array for(int y = 0; y < physicsRadius * 2 + 1; y++){ needsPhysics[physicsRadius * 2][y] = true; hasPhysics[physicsRadius * 2][y] = false; } } public void shiftChunksNegY(){ //retire cells for(int x = 0; x < drawRadius * 2 + 1; x++){ if(cells[x][drawRadius * 2] != null){ cells[x][drawRadius * 2].retireCell(); } } //shift draw array for(int x = 0; x < drawRadius * 2 + 1; x++){ for(int y = drawRadius * 2; y > 0; y--){ cells[x][y] = cells[x][y-1]; updateable[x][y] = true; hasRequested[x][y] = hasRequested[x][y-1]; } } //invalidate edge of draw array for(int x = 0; x < drawRadius * 2 + 1; x++){ valid[x][0] = false; hasRequested[x][0] = false; } //retire physics of cells for(int x = 0; x < physicsRadius * 2 + 1; x++){ if(hasPhysics[x][physicsRadius * 2]){ cells[x + drawRadius - physicsRadius][physicsRadius * 2 + drawRadius - physicsRadius].destroyPhysics(); } } //shift physics array for(int x = 0; x < physicsRadius * 2 + 1; x++){ for(int y = physicsRadius * 2; y > 0; y--){ needsPhysics[x][y] = needsPhysics[x][y-1]; hasPhysics[x][y] = hasPhysics[x][y-1]; } } //invalidate edge of physics array for(int x = 0; x < physicsRadius * 2 + 1; x++){ needsPhysics[x][0] = true; hasPhysics[x][0] = false; } } public void shiftChunksPosY(){ //retire old graphics for(int x = 0; x < drawRadius * 2 + 1; x++){ if(cells[x][0] != null){ cells[x][0].retireCell(); } } //shift draw array for(int x = 0; x < drawRadius * 2 + 1; x++){ for(int y = 0; y < drawRadius * 2; y++){ cells[x][y] = cells[x][y+1]; updateable[x][y] = true; hasRequested[x][y] = hasRequested[x][y+1]; } } //invalidate edge of draw array for(int x = 0; x < drawRadius * 2 + 1; x++){ valid[x][drawRadius * 2] = false; hasRequested[x][drawRadius * 2] = false; } //retire physics of cells for(int x = 0; x < physicsRadius * 2 + 1; x++){ if(hasPhysics[x][0]){ cells[x + drawRadius - physicsRadius][0 + drawRadius - physicsRadius].destroyPhysics(); } } //shift physics array for(int x = 0; x < physicsRadius * 2 + 1; x++){ for(int y = 0; y < physicsRadius * 2; y++){ needsPhysics[x][y] = needsPhysics[x][y+1]; hasPhysics[x][y] = hasPhysics[x][y+1]; } } //invalidate edge of physics array for(int x = 0; x < physicsRadius * 2 + 1; x++){ needsPhysics[x][physicsRadius * 2] = true; hasPhysics[x][physicsRadius * 2] = false; } } public int transformRealSpaceToCellSpace(float input){ return (int)(input / clientWorldData.getDynamicInterpolationRatio()); } public void invalidateAllCells(){ for(int x = 0; x < drawRadius * 2 + 1; x++){ for(int y = 0; y < drawRadius * 2 + 1; y++){ valid[x][y] = false; } } } public void calculateDeltas(Vector3f oldPosition, Vector3f newPosition){ // if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){ if(transformRealSpaceToCellSpace(newPosition.x()) < transformRealSpaceToCellSpace(oldPosition.x())){ shiftChunksNegX(); setCellX(transformRealSpaceToCellSpace(newPosition.x())); setCellY(transformRealSpaceToCellSpace(newPosition.z())); } else if(transformRealSpaceToCellSpace(newPosition.x()) > transformRealSpaceToCellSpace(oldPosition.x())){ shiftChunksPosX(); setCellX(transformRealSpaceToCellSpace(newPosition.x())); setCellY(transformRealSpaceToCellSpace(newPosition.z())); } if(transformRealSpaceToCellSpace(newPosition.z()) < transformRealSpaceToCellSpace(oldPosition.z())){ shiftChunksNegY(); setCellX(transformRealSpaceToCellSpace(newPosition.x())); setCellY(transformRealSpaceToCellSpace(newPosition.z())); } else if(transformRealSpaceToCellSpace(newPosition.z()) > transformRealSpaceToCellSpace(oldPosition.z())){ shiftChunksPosY(); setCellX(transformRealSpaceToCellSpace(newPosition.x())); setCellY(transformRealSpaceToCellSpace(newPosition.z())); } // } } public void update(){ if(update){ // if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){ if(containsInvalidCell()){ updateInvalidCell(); } else if(containsUndrawableCell()){ makeCellDrawable(); } else if(containsUpdateableCell()){ updateCellModel(); } else if(containsPhysicsNeedingCell()){ addPhysicsToCell(); } } // } else if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){ // if(cells[0][0] == null){ // int arenaChunkWidth = 100; // cells[0][0] = new DrawCell( // new float[arenaChunkWidth + 1][arenaChunkWidth + 1], // arenaChunkWidth + 1, // 0, // 0, // arenaChunkWidth, // program // ); // cells[0][0].generateDrawableEntity(1); // } else if(cells[0][1] == null){ // int arenaChunkWidth = 100; // cells[0][1] = new DrawCell( // new float[arenaChunkWidth + 1][arenaChunkWidth + 1], // arenaChunkWidth + 1, // 0, // 1, // arenaChunkWidth, // program // ); // cells[0][1].generateDrawableEntity(1); // } else if(cells[1][0] == null){ // int arenaChunkWidth = 100; // cells[1][0] = new DrawCell( // new float[arenaChunkWidth + 1][arenaChunkWidth + 1], // arenaChunkWidth + 1, // 1, // 0, // arenaChunkWidth, // program // ); // cells[1][0].generateDrawableEntity(1); // } else if(cells[1][1] == null){ // int arenaChunkWidth = 100; // cells[1][1] = new DrawCell( // new float[arenaChunkWidth + 1][arenaChunkWidth + 1], // arenaChunkWidth + 1, // 1, // 1, // arenaChunkWidth, // program // ); // cells[1][1].generateDrawableEntity(1); // } // } } // public float getElevationAtRealPoint(float realPointX, float realPointY){ //// if(DRAW_CELL_MANAGER_FLAG_UPDATE_DYNAMIC_TERRAIN){ // return terrainManager.getHeightAtPosition(realPointX, realPointY); //// } //// if(DRAW_CELL_MANAGER_FLAG_GENERATE_ARENA){ //// return 0; //// } //// return 0; // } public boolean canValidateCell(){ boolean rVal = false; return rVal; } public boolean coordsInPhysicsSpace(int worldX, int worldY){ return worldX <= cellX + physicsRadius && worldX >= cellX - physicsRadius && worldY <= cellY + physicsRadius && worldY >= cellY - physicsRadius; } // public }