From 032017380092051b7142b5c02fd43e68858b037e Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 14:47:08 -0500 Subject: [PATCH 01/21] Add stride to reduced endpoint --- .../client/terrain/cache/ChunkData.java | 5 ++ .../net/client/protocol/TerrainProtocol.java | 4 ++ .../net/server/protocol/TerrainProtocol.java | 67 ++++++++++++++++++- .../generation/DefaultChunkGenerator.java | 2 +- .../generation/OverworldChunkGenerator.java | 2 +- .../TestGenerationChunkGenerator.java | 2 +- .../generation/interfaces/ChunkGenerator.java | 3 +- .../manager/ChunkGenerationThread.java | 10 ++- .../terrain/manager/ServerTerrainManager.java | 8 ++- 9 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java index 4036d6a0..5f094f24 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java +++ b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java @@ -12,6 +12,11 @@ import electrosphere.server.terrain.manager.ServerTerrainChunk; */ public class ChunkData { + /** + * No stride + */ + public static final int NO_STRIDE = 0; + //The size of a chunk in virtual data public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION; //The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index cdd606f2..aee25351 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -47,6 +47,10 @@ public class TerrainProtocol implements ClientProtocolTemplate { LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ()); Globals.clientTerrainManager.attachTerrainMessage(message); } break; + case SENDREDUCEDCHUNKDATA: { + LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ() + " " + message.getchunkResolution()); + Globals.clientTerrainManager.attachTerrainMessage(message); + } break; case UPDATEVOXEL: { // //find what all drawcells might be updated by this voxel update diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index 99b1d4f8..610199d3 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -7,6 +7,7 @@ import java.util.function.Consumer; import org.joml.Vector3d; +import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; @@ -32,6 +33,12 @@ public class TerrainProtocol implements ServerProtocolTemplate { ); return null; } + case REQUESTREDUCEDCHUNKDATA: { + sendWorldSubChunkAsyncStrided(connectionHandler, + message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution() + ); + return null; + } default: { } break; } @@ -185,7 +192,65 @@ public class TerrainProtocol implements ServerProtocolTemplate { }; //request chunk - realm.getServerWorldData().getServerTerrainManager().getChunkAsync(worldX, worldY, worldZ, onLoad); + realm.getServerWorldData().getServerTerrainManager().getChunkAsync(worldX, worldY, worldZ, ChunkData.NO_STRIDE, onLoad); + + Globals.profiler.endCpuSample(); + } + + /** + * Sends a subchunk to the client + * @param connectionHandler The connection handler + * @param worldX the world x + * @param worldY the world y + * @param worldZ the world z + * @param stride The stride of the data + */ + static void sendWorldSubChunkAsyncStrided(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ, int stride){ + Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk"); + + // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); + Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer()); + if(realm.getServerWorldData().getServerTerrainManager() == null){ + return; + } + + Consumer onLoad = (ServerTerrainChunk chunk) -> { + //The length along each access of the chunk data. Typically, should be at least 17. + //Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate + //chunk data that connects seamlessly to the next chunk. + int xWidth = chunk.getWeights().length; + int yWidth = chunk.getWeights()[0].length; + int zWidth = chunk.getWeights()[0][0].length; + + ByteBuffer buffer = ByteBuffer.allocate(xWidth*yWidth*zWidth*(4+4)); + FloatBuffer floatView = buffer.asFloatBuffer(); + + for(int x = 0; x < xWidth; x++){ + for(int y = 0; y < yWidth; y++){ + for(int z = 0; z < zWidth; z++){ + floatView.put(chunk.getWeights()[x][y][z]); + } + } + } + + IntBuffer intView = buffer.asIntBuffer(); + intView.position(floatView.position()); + + for(int x = 0; x < xWidth; x++){ + for(int y = 0; y < yWidth; y++){ + for(int z = 0; z < zWidth; z++){ + intView.put(chunk.getValues()[x][y][z]); + } + } + } + + // System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ); + LoggerInterface.loggerNetworking.DEBUG("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ); + connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendChunkDataMessage(worldX, worldY, worldZ, buffer.array())); + }; + + //request chunk + realm.getServerWorldData().getServerTerrainManager().getChunkAsync(worldX, worldY, worldZ, stride, onLoad); Globals.profiler.endCpuSample(); } diff --git a/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java index c3c2518b..2a5f5b5a 100644 --- a/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java @@ -22,7 +22,7 @@ public class DefaultChunkGenerator implements ChunkGenerator { } @Override - public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. //Hence, width should actually be chunk dimension + 1 float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; diff --git a/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java index 15bd5d21..6674257a 100644 --- a/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java @@ -28,7 +28,7 @@ public class OverworldChunkGenerator implements ChunkGenerator { } @Override - public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { ServerTerrainChunk returnedChunk; //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. //Hence, width should actually be chunk dimension + 1 diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 669c6fb1..aa299212 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -65,7 +65,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } @Override - public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk"); ServerTerrainChunk rVal = null; float[][][] weights; diff --git a/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java index bb002835..d6e5afa3 100644 --- a/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java @@ -13,9 +13,10 @@ public interface ChunkGenerator { * @param worldX The x component * @param worldY The y component * @param worldZ The z component + * @param stride The stride of the data * @return The chunk */ - public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ); + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride); /** * Sets the terrain model for the generation algorithm diff --git a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java index b66556d3..5446f56e 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java @@ -51,6 +51,11 @@ public class ChunkGenerationThread implements Runnable { * The world z coordinate */ int worldZ; + + /** + * The stride of the data + */ + int stride; /** * The work to do once the chunk is available @@ -65,6 +70,7 @@ public class ChunkGenerationThread implements Runnable { * @param worldX The world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate + * @param stride The stride of the data * @param onLoad The work to do once the chunk is available */ public ChunkGenerationThread( @@ -72,6 +78,7 @@ public class ChunkGenerationThread implements Runnable { ServerChunkCache chunkCache, ChunkGenerator chunkGenerator, int worldX, int worldY, int worldZ, + int stride, Consumer onLoad ){ this.chunkDiskMap = chunkDiskMap; @@ -80,6 +87,7 @@ public class ChunkGenerationThread implements Runnable { this.worldX = worldX; this.worldY = worldY; this.worldZ = worldZ; + this.stride = stride; this.onLoad = onLoad; } @@ -99,7 +107,7 @@ public class ChunkGenerationThread implements Runnable { } //generate if it does not exist if(chunk == null){ - chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ); + chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, stride); } if(chunk != null){ chunkCache.add(worldX, worldY, worldZ, chunk); diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index 2c9adec1..3a3ad4b9 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -1,5 +1,6 @@ package electrosphere.server.terrain.manager; +import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; import electrosphere.game.server.world.ServerWorldData; import electrosphere.server.terrain.diskmap.ChunkDiskMap; @@ -242,7 +243,7 @@ public class ServerTerrainManager { } //generate if it does not exist if(returnedChunk == null){ - returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ); + returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, ChunkData.NO_STRIDE); } this.chunkCache.add(worldX, worldY, worldZ, returnedChunk); } @@ -255,11 +256,12 @@ public class ServerTerrainManager { * @param worldX The world x position * @param worldY The world y position * @param worldZ The world z position + * @param stride The stride of the data * @param onLoad The logic to run once the chunk is available */ - public void getChunkAsync(int worldX, int worldY, int worldZ, Consumer onLoad){ + public void getChunkAsync(int worldX, int worldY, int worldZ, int stride, Consumer onLoad){ Globals.profiler.beginCpuSample("ServerTerrainManager.getChunkAsync"); - this.chunkExecutorService.submit(new ChunkGenerationThread(chunkDiskMap, chunkCache, chunkGenerator, worldX, worldY, worldZ, onLoad)); + this.chunkExecutorService.submit(new ChunkGenerationThread(chunkDiskMap, chunkCache, chunkGenerator, worldX, worldY, worldZ, stride, onLoad)); Globals.profiler.endCpuSample(); } From e14c8ec68ab60aab9665b0afdb835ae1d864dbe7 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 14:54:17 -0500 Subject: [PATCH 02/21] use new endpoint for client terrain manager --- .../terrain/cells/ClientDrawCellManager.java | 3 +- .../terrain/manager/ClientTerrainManager.java | 37 +++++++++++++++++-- .../net/server/protocol/TerrainProtocol.java | 2 +- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 92ab7eb1..b7e584cc 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -6,6 +6,7 @@ import java.util.List; import org.joml.Vector3d; import org.joml.Vector3i; +import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cells.DrawCell.DrawCellFace; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; @@ -526,7 +527,7 @@ public class ClientDrawCellManager { ){ //client should request chunk data from server for each chunk necessary to create the model LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); - Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z); + Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE); } } } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 5fdba3c1..d8e5ff67 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -106,6 +106,35 @@ public class ClientTerrainManager { data ); } break; + case SENDREDUCEDCHUNKDATA: { + int[][][] values = new int[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE]; + float[][][] weights = new float[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE]; + ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData()); + FloatBuffer floatBuffer = buffer.asFloatBuffer(); + for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ + weights[x][y][z] = floatBuffer.get(); + } + } + } + IntBuffer intView = buffer.asIntBuffer(); + intView.position(floatBuffer.position()); + for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ + values[x][y][z] = intView.get(); + } + } + } + ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ()); + data.setVoxelType(values); + data.setVoxelWeight(weights); + terrainCache.addChunkDataToCache( + message.getworldX(), message.getworldY(), message.getworldZ(), + data + ); + } break; default: LoggerInterface.loggerEngine.WARNING("ClientTerrainManager: unhandled network message of type" + message.getMessageSubtype()); break; @@ -160,13 +189,15 @@ public class ClientTerrainManager { * @param worldX the world x coordinate of the chunk * @param worldY the world y coordinate of the chunk * @param worldZ the world z coordinate of the chunk + * @param stride The stride of the data */ - public void requestChunk(int worldX, int worldY, int worldZ){ + public void requestChunk(int worldX, int worldY, int worldZ, int stride){ if(!this.terrainCache.hasRequested(worldX, worldY, worldZ)){ - Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage( + Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( worldX, worldY, - worldZ + worldZ, + stride )); } } diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index 610199d3..7c91d0e2 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -246,7 +246,7 @@ public class TerrainProtocol implements ServerProtocolTemplate { // System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ); LoggerInterface.loggerNetworking.DEBUG("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ); - connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendChunkDataMessage(worldX, worldY, worldZ, buffer.array())); + connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructSendReducedChunkDataMessage(worldX, worldY, worldZ, stride, buffer.array())); }; //request chunk From db2886c0c7ec7bb1b4b6cbee5d949962138759f0 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 14:56:26 -0500 Subject: [PATCH 03/21] remove requested blocking --- .../terrain/cache/ClientTerrainCache.java | 38 +------------------ .../terrain/manager/ClientTerrainManager.java | 22 +++-------- .../engine/loadingthreads/ClientLoading.java | 2 +- 3 files changed, 8 insertions(+), 54 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index 90f1dcf4..802b8f44 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -24,11 +24,6 @@ public class ClientTerrainCache { List cacheList = new CopyOnWriteArrayList(); //A map of chunk to its world position Map chunkPositionMap = new ConcurrentHashMap(); - - /** - * The map tracking chunks that have been requested - */ - Map requestedChunks = new ConcurrentHashMap(); /** * Constructor @@ -50,9 +45,7 @@ public class ClientTerrainCache { chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ)); while(cacheList.size() > cacheSize){ Long currentChunk = cacheList.remove(0); - ChunkData data = cacheMap.remove(currentChunk); - Vector3i worldPos = data.getWorldPos(); - requestedChunks.remove(getKey(worldPos.x,worldPos.y,worldPos.z)); + cacheMap.remove(currentChunk); } } @@ -119,34 +112,5 @@ public class ClientTerrainCache { public Vector3i getChunkPosition(ChunkData chunk){ return chunkPositionMap.get(chunk); } - - /** - * Gets the number of cells that have been requested - * @return The number of cells that have been requested - */ - public int getRequestedCellCount(){ - return this.requestedChunks.size(); - } - - /** - * Checks if a chunk has been requested or not - * @param worldX The x coordinate in the world - * @param worldY The y coordinate in the world - * @param worldZ The z coordinate in the world - * @return true if it has been requested, false otherwise - */ - public boolean hasRequested(int worldX, int worldY, int worldZ){ - return this.requestedChunks.containsKey(getKey(worldX, worldY, worldZ)); - } - - /** - * Marks a chunk as requested - * @param worldX The x coordinate in the world - * @param worldY The y coordinate in the world - * @param worldZ The z coordinate in the world - */ - public void markAsRequested(int worldX, int worldY, int worldZ){ - this.requestedChunks.put(getKey(worldX, worldY, worldZ),true); - } } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index d8e5ff67..9c08f66c 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -192,14 +192,12 @@ public class ClientTerrainManager { * @param stride The stride of the data */ public void requestChunk(int worldX, int worldY, int worldZ, int stride){ - if(!this.terrainCache.hasRequested(worldX, worldY, worldZ)){ - Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( - worldX, - worldY, - worldZ, - stride - )); - } + Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( + worldX, + worldY, + worldZ, + stride + )); } /** @@ -287,13 +285,5 @@ public class ClientTerrainManager { public Vector3i getPositionOfChunk(ChunkData chunk){ return terrainCache.getChunkPosition(chunk); } - - /** - * Gets the number of chunks that have been requested - * @return The number of chunks - */ - public int getRequestedCellCount(){ - return this.terrainCache.getRequestedCellCount(); - } } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 9d6523d7..80fca928 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -300,7 +300,7 @@ public class ClientLoading { while(blockForInit && Globals.clientDrawCellManager.updatedLastFrame() && Globals.threadManager.shouldKeepRunning()){ i++; if(i % DRAW_CELL_UPDATE_RATE == 0){ - WindowUtils.updateLoadingWindow("WAITING ON SERVER TO SEND TERRAIN (" + Globals.clientTerrainManager.getAllChunks().size() + "/" + Globals.clientTerrainManager.getRequestedCellCount() + ")"); + WindowUtils.updateLoadingWindow("WAITING ON SERVER TO SEND TERRAIN (" + Globals.clientTerrainManager.getAllChunks().size() + ")"); } try { TimeUnit.MILLISECONDS.sleep(10); From c80c8e5b7163eed4cc16e534ec38d1be227b448e Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 14:59:48 -0500 Subject: [PATCH 04/21] chunk data stride tracking --- .../client/terrain/cache/ChunkData.java | 17 ++++++++++++++++- .../terrain/manager/ClientTerrainManager.java | 4 ++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java index 5f094f24..3c9f5762 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java +++ b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java @@ -44,16 +44,23 @@ public class ChunkData { */ int worldZ; + /** + * The stride of the data + */ + int stride; + /** * Creates a chunk data * @param worldX The word x coordinate * @param worldY The word y coordinate * @param worldZ The word z coordinate + * @param stride The stride of the data */ - public ChunkData(int worldX, int worldY, int worldZ){ + public ChunkData(int worldX, int worldY, int worldZ, int stride){ this.worldX = worldX; this.worldY = worldY; this.worldZ = worldZ; + this.stride = stride; } @@ -216,5 +223,13 @@ public class ChunkData { return new Vector3i(worldX,worldY,worldZ); } + /** + * Gets the stride of the data + * @return The stride of the data + */ + public int getStride(){ + return stride; + } + } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 9c08f66c..60d5719a 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -98,7 +98,7 @@ public class ClientTerrainManager { } } } - ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ()); + ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), ChunkData.NO_STRIDE); data.setVoxelType(values); data.setVoxelWeight(weights); terrainCache.addChunkDataToCache( @@ -127,7 +127,7 @@ public class ClientTerrainManager { } } } - ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ()); + ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution()); data.setVoxelType(values); data.setVoxelWeight(weights); terrainCache.addChunkDataToCache( From 40e9b1cba45a250eae0b278510009cee0c616b75 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 15:01:08 -0500 Subject: [PATCH 05/21] comment work --- .../terrain/cache/ClientTerrainCache.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index 802b8f44..c1628975 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -16,13 +16,24 @@ import io.github.studiorailgun.HashUtils; */ public class ClientTerrainCache { - //cache capacity + /** + * Cache capacity + */ int cacheSize; - //the map of chunk key -> chunk data + + /** + * The map of chunk key -> chunk data + */ Map cacheMap = new ConcurrentHashMap(); - //the list of keys in the cache + + /** + * The list of keys in the cache + */ List cacheList = new CopyOnWriteArrayList(); - //A map of chunk to its world position + + /** + * A map of chunk to its world position + */ Map chunkPositionMap = new ConcurrentHashMap(); /** From 8ca29e4b40aaa6247328abd7fc51aa270cd65457 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 15:10:32 -0500 Subject: [PATCH 06/21] chunkdata stride data tracking --- .../client/fluid/cells/FluidCellManager.java | 2 +- .../client/foliagemanager/FoliageCell.java | 2 +- .../client/foliagemanager/FoliageChunk.java | 6 +- .../terrain/cache/ClientTerrainCache.java | 48 ++++++++--- .../terrain/cells/ClientDrawCellManager.java | 8 +- .../client/terrain/cells/DrawCell.java | 81 ++++++++++++------- .../client/terrain/cells/DrawCellManager.java | 6 +- .../terrain/manager/ClientTerrainManager.java | 36 +++------ .../terrain/sampling/ClientVoxelSampler.java | 4 +- .../net/client/protocol/TerrainProtocol.java | 8 +- 10 files changed, 118 insertions(+), 83 deletions(-) diff --git a/src/main/java/electrosphere/client/fluid/cells/FluidCellManager.java b/src/main/java/electrosphere/client/fluid/cells/FluidCellManager.java index ef4556ba..6b856f64 100644 --- a/src/main/java/electrosphere/client/fluid/cells/FluidCellManager.java +++ b/src/main/java/electrosphere/client/fluid/cells/FluidCellManager.java @@ -404,7 +404,7 @@ public class FluidCellManager { * @return The chunk data at the specified points */ ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){ - return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ); + return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE); } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 2205ad8d..7b652ab8 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -205,7 +205,7 @@ public class FoliageCell { protected void generate(){ boolean shouldGenerate = false; //get foliage types supported - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition); + ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition,ChunkData.NO_STRIDE); if(data == null){ return; } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java index 67979564..de56289b 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java @@ -77,9 +77,9 @@ public class FoliageChunk { */ public void initCells(){ Globals.profiler.beginCpuSample("FoliageChunk.initCells"); - this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); + this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos,ChunkData.NO_STRIDE); // //evaluate top cells if chunk above this one exists - this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0)); + this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0),ChunkData.NO_STRIDE); this.updateCells(true); Globals.profiler.endCpuSample(); } @@ -113,7 +113,7 @@ public class FoliageChunk { * @return true if contains foliage voxel, false otherwise */ private boolean checkContainsFoliageVoxel(){ - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos()); + ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos(),ChunkData.NO_STRIDE); if(data == null){ return false; } diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index c1628975..a8d2509a 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -22,9 +22,14 @@ public class ClientTerrainCache { int cacheSize; /** - * The map of chunk key -> chunk data + * The map of full res chunk key -> chunk data */ - Map cacheMap = new ConcurrentHashMap(); + Map cacheMapFullRes = new ConcurrentHashMap(); + + /** + * The map of half res chunk key -> chunk data + */ + Map cacheMapHalfRes = new ConcurrentHashMap(); /** * The list of keys in the cache @@ -52,11 +57,12 @@ public class ClientTerrainCache { * @param chunkData The chunk data to add at the specified positions */ public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){ - cacheMap.put(getKey(worldX,worldY,worldZ),chunkData); + Map cache = this.getCache(chunkData.getStride()); + cache.put(getKey(worldX,worldY,worldZ),chunkData); chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ)); while(cacheList.size() > cacheSize){ Long currentChunk = cacheList.remove(0); - cacheMap.remove(currentChunk); + cache.remove(currentChunk); } } @@ -65,7 +71,8 @@ public class ClientTerrainCache { */ public void evictAll(){ this.cacheList.clear(); - this.cacheMap.clear(); + this.cacheMapFullRes.clear(); + this.cacheMapHalfRes.clear(); this.chunkPositionMap.clear(); } @@ -86,10 +93,11 @@ public class ClientTerrainCache { * @param worldX The x world position * @param worldY The y world position * @param worldZ The z world position + * @param stride The stride of the data * @return True if the cache contains chunk data at the specified point, false otherwise */ - public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ - return cacheMap.containsKey(getKey(worldX,worldY,worldZ)); + public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){ + return this.getCache(stride).containsKey(getKey(worldX,worldY,worldZ)); } @@ -101,10 +109,11 @@ public class ClientTerrainCache { * @param worldX The x world position * @param worldY The y world position * @param worldZ The z world position + * @param stride The stride of the data * @return The chunk data if it exists, null otherwise */ - public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){ - return cacheMap.get(getKey(worldX,worldY,worldZ)); + public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ, int stride){ + return this.getCache(stride).get(getKey(worldX,worldY,worldZ)); } /** @@ -112,7 +121,7 @@ public class ClientTerrainCache { * @return The list of all chunks in the cache */ public Collection getAllChunks(){ - return this.cacheMap.values(); + return this.cacheMapFullRes.values(); } /** @@ -124,4 +133,23 @@ public class ClientTerrainCache { return chunkPositionMap.get(chunk); } + /** + * Gets the cache + * @param stride The stride of the data + * @return The cache to use + */ + public Map getCache(int stride){ + switch(stride){ + case 0: { + return cacheMapFullRes; + } + case 1: { + return cacheMapHalfRes; + } + default: { + throw new Error("Invalid stride probided! " + stride); + } + } + } + } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index b7e584cc..73602ec1 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -500,7 +500,7 @@ public class ClientDrawCellManager { */ boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ if(Globals.clientTerrainManager != null){ - return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); + return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE); } return true; } @@ -523,7 +523,7 @@ public class ClientDrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) ){ //client should request chunk data from server for each chunk necessary to create the model LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); @@ -554,7 +554,7 @@ public class ClientDrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) ){ return false; } @@ -595,7 +595,7 @@ public class ClientDrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) ){ return false; } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 96eed5b9..0bb6b3ef 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -194,7 +194,8 @@ public class DrawCell { ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( worldPos.x + (x * spacingFactor) / ChunkData.CHUNK_SIZE, worldPos.y + (y * spacingFactor) / ChunkData.CHUNK_SIZE, - worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE + worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE, + ChunkData.NO_STRIDE ); if(currentChunk == null){ return false; @@ -248,11 +249,14 @@ public class DrawCell { //implicitly performing transforms to adapt from face-space to world & local space switch(higherLODFace){ case X_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - worldPos.y + worldCoordOffset1, - worldPos.z + worldCoordOffset2 - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, + worldPos.y + worldCoordOffset1, + worldPos.z + worldCoordOffset2 + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } @@ -268,11 +272,14 @@ public class DrawCell { ); } break; case X_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x, - worldPos.y + worldCoordOffset1, - worldPos.z + worldCoordOffset2 - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x, + worldPos.y + worldCoordOffset1, + worldPos.z + worldCoordOffset2 + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } @@ -288,11 +295,14 @@ public class DrawCell { ); } break; case Y_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - worldPos.z + worldCoordOffset2 - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, + worldPos.z + worldCoordOffset2 + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } @@ -308,11 +318,14 @@ public class DrawCell { ); } break; case Y_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y, - worldPos.z + worldCoordOffset2 - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y, + worldPos.z + worldCoordOffset2 + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } @@ -328,11 +341,14 @@ public class DrawCell { ); } break; case Z_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + worldCoordOffset2, - worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + worldCoordOffset2, + worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } @@ -348,11 +364,14 @@ public class DrawCell { ); } break; case Z_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + worldCoordOffset2, - worldPos.z - )); + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + worldCoordOffset2, + worldPos.z + ), + ChunkData.NO_STRIDE + ); if(currentChunk == null){ return false; } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java index de4c287e..0d514141 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java @@ -176,7 +176,7 @@ public class DrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) ){ if(!requested.contains(requestKey)){ //client should request chunk data from server for each chunk necessary to create the model @@ -429,7 +429,7 @@ public class DrawCellManager { */ boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ if(Globals.clientTerrainManager != null){ - return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); + return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE); } return true; } @@ -442,7 +442,7 @@ public class DrawCellManager { * @return The chunk data at the specified points */ ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){ - return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ); + return Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE); } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 60d5719a..8bdd02a7 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -169,19 +169,21 @@ public class ClientTerrainManager { * @param worldX the x position * @param worldY the y position * @param worldZ the z position + * @param stride The stride of the data * @return true if the data exists, false otherwise */ - public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ - return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ); + public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){ + return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ, stride); } /** * Checks if the terrain cache contains chunk data at a given world position * @param worldPos The vector containing the world-space position + * @param stride The stride of the data * @return true if the data exists, false otherwise */ - public boolean containsChunkDataAtWorldPoint(Vector3i worldPos){ - return terrainCache.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z); + public boolean containsChunkDataAtWorldPoint(Vector3i worldPos, int stride){ + return this.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, stride); } /** @@ -200,40 +202,26 @@ public class ClientTerrainManager { )); } - /** - * Checks that the cache contains chunk data at a real-space coordinate - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @return true if the cache contains the chunk data at the coordinate, false otherwise - */ - public boolean containsChunkDataAtRealPoint(double x, double y, double z){ - assert clientWorldData != null; - return terrainCache.containsChunkDataAtWorldPoint( - clientWorldData.convertRealToChunkSpace(x), - clientWorldData.convertRealToChunkSpace(y), - clientWorldData.convertRealToChunkSpace(z) - ); - } - /** * Gets the chunk data at a given world position * @param worldX The x component of the world coordinate * @param worldY The y component of the world coordinate * @param worldZ The z component of the world coordinate + * @param stride The stride of the data * @return The chunk data if it exists, otherwise null */ - public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ - return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ); + public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){ + return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ, stride); } /** * Gets the chunk data at a given world position * @param worldPos The world position as a joml vector + * @param stride The stride of the data * @return The chunk data if it exists, otherwise null */ - public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos){ - return terrainCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z); + public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos, int stride){ + return this.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, stride); } diff --git a/src/main/java/electrosphere/client/terrain/sampling/ClientVoxelSampler.java b/src/main/java/electrosphere/client/terrain/sampling/ClientVoxelSampler.java index 8834bd06..d8fd6dab 100644 --- a/src/main/java/electrosphere/client/terrain/sampling/ClientVoxelSampler.java +++ b/src/main/java/electrosphere/client/terrain/sampling/ClientVoxelSampler.java @@ -36,8 +36,8 @@ public class ClientVoxelSampler { int voxelId = 0; Vector3i chunkSpacePos = Globals.clientWorldData.convertRealToWorldSpace(realPos); Vector3i voxelSpacePos = Globals.clientWorldData.convertRealToVoxelSpace(realPos); - if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(chunkSpacePos)){ - ChunkData chunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkSpacePos); + if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE)){ + ChunkData chunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE); voxelId = chunkData.getType(voxelSpacePos); } else { return INVALID_POSITION; diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index aee25351..45a1c6a2 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -83,8 +83,8 @@ public class TerrainProtocol implements ClientProtocolTemplate { } // //update the terrain cache - if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z)){ - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z); + if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE)){ + ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE); if(data != null){ data.updatePosition( message.getvoxelX(), @@ -98,8 +98,8 @@ public class TerrainProtocol implements ClientProtocolTemplate { // //mark all relevant drawcells as updateable for(Vector3i worldPosToUpdate : positionsToUpdate){ - if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z)){ - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z); + if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z, ChunkData.NO_STRIDE)){ + ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z, ChunkData.NO_STRIDE); if(data != null){ Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z); } From bf292f29c66f81f8db45b0e1cff3cb4047994bfb Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 15:38:26 -0500 Subject: [PATCH 07/21] alter index lookups on client side --- .../terrain/cells/ClientDrawCellManager.java | 120 +++--- .../client/terrain/cells/DrawCell.java | 406 +++++++++--------- 2 files changed, 257 insertions(+), 269 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 73602ec1..fee5ce7f 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -491,31 +491,18 @@ public class ClientDrawCellManager { throw new Error("Unimplemented"); } - /** - * Checks if the terrain cache has a chunk at a given world point - * @param worldX the x coordinate - * @param worldY the y coordinate - * @param worldZ the z coordinate - * @return true if the chunk data exists, false otherwise - */ - boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ - if(Globals.clientTerrainManager != null){ - return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE); - } - return true; - } - /** * Requests all chunks for a given draw cell * @param cell The cell */ private void requestChunks(WorldOctTree.FloatingChunkTreeNode node){ DrawCell cell = node.getData(); - int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1; - for(int i = 0; i < 2 * lodMultiplitier; i++){ - for(int j = 0; j < 2 * lodMultiplitier; j++){ - for(int k = 0; k < 2 * lodMultiplitier; k++){ - Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k); + int lod = this.chunkTree.getMaxLevel() - node.getLevel(); + int spacingFactor = (int)Math.pow(2,lod); + for(int i = 0; i < 2; i++){ + for(int j = 0; j < 2; j++){ + for(int k = 0; k < 2; k++){ + Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i*spacingFactor,j*spacingFactor,k*spacingFactor); if( posToCheck.x >= 0 && posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && @@ -542,11 +529,12 @@ public class ClientDrawCellManager { */ private boolean containsDataToGenerate(WorldOctTree.FloatingChunkTreeNode node, List highResFaces){ DrawCell cell = node.getData(); - int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1; - for(int i = 0; i < 2 * lodMultiplitier; i++){ - for(int j = 0; j < 2 * lodMultiplitier; j++){ - for(int k = 0; k < 2 * lodMultiplitier; k++){ - Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k); + int lod = this.chunkTree.getMaxLevel() - node.getLevel(); + int spacingFactor = (int)Math.pow(2,lod); + for(int i = 0; i < 2; i++){ + for(int j = 0; j < 2; j++){ + for(int k = 0; k < 2; k++){ + Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i*spacingFactor,j*spacingFactor,k*spacingFactor); if( posToCheck.x >= 0 && posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && @@ -561,48 +549,48 @@ public class ClientDrawCellManager { } } } - if(highResFaces != null){ - for(DrawCellFace highResFace : highResFaces){ - //x & y are in face-space - for(int x = 0; x < 2 * lodMultiplitier; x++){ - for(int y = 0; y < 2 * lodMultiplitier; y++){ - Vector3i posToCheck = null; - //implicitly performing transforms to adapt from face-space to world space - switch(highResFace){ - case X_POSITIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(lodMultiplitier,x,y); - } break; - case X_NEGATIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(-1,x,y); - } break; - case Y_POSITIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(x,lodMultiplitier,y); - } break; - case Y_NEGATIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(x,-1,y); - } break; - case Z_POSITIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,lodMultiplitier); - } break; - case Z_NEGATIVE: { - posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,-1); - } break; - } - if( - posToCheck.x >= 0 && - posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && - posToCheck.y >= 0 && - posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && - posToCheck.z >= 0 && - posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) - ){ - return false; - } - } - } - } - } + // if(highResFaces != null){ + // for(DrawCellFace highResFace : highResFaces){ + // //x & y are in face-space + // for(int x = 0; x < 4; x++){ + // for(int y = 0; y < 4; y++){ + // Vector3i posToCheck = null; + // //implicitly performing transforms to adapt from face-space to world space + // switch(highResFace){ + // case X_POSITIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(lodMultiplitier,x,y); + // } break; + // case X_NEGATIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(-1,x,y); + // } break; + // case Y_POSITIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(x,lodMultiplitier,y); + // } break; + // case Y_NEGATIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(x,-1,y); + // } break; + // case Z_POSITIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,lodMultiplitier); + // } break; + // case Z_NEGATIVE: { + // posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,-1); + // } break; + // } + // if( + // posToCheck.x >= 0 && + // posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && + // posToCheck.y >= 0 && + // posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && + // posToCheck.z >= 0 && + // posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && + // !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) + // ){ + // return false; + // } + // } + // } + // } + // } return true; } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 0bb6b3ef..81c63096 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -192,23 +192,23 @@ public class DrawCell { for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){ for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){ ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - worldPos.x + (x * spacingFactor) / ChunkData.CHUNK_SIZE, - worldPos.y + (y * spacingFactor) / ChunkData.CHUNK_SIZE, - worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE, + worldPos.x + x / ChunkData.CHUNK_SIZE * spacingFactor, + worldPos.y + y / ChunkData.CHUNK_SIZE * spacingFactor, + worldPos.z + z / ChunkData.CHUNK_SIZE * spacingFactor, ChunkData.NO_STRIDE ); if(currentChunk == null){ return false; } weights[x][y][z] = currentChunk.getWeight( - (x * spacingFactor) % ChunkData.CHUNK_SIZE, - (y * spacingFactor) % ChunkData.CHUNK_SIZE, - (z * spacingFactor) % ChunkData.CHUNK_SIZE + x % ChunkData.CHUNK_SIZE, + y % ChunkData.CHUNK_SIZE, + z % ChunkData.CHUNK_SIZE ); types[x][y][z] = currentChunk.getType( - (x * spacingFactor) % ChunkData.CHUNK_SIZE, - (y * spacingFactor) % ChunkData.CHUNK_SIZE, - (z * spacingFactor) % ChunkData.CHUNK_SIZE + x % ChunkData.CHUNK_SIZE, + y % ChunkData.CHUNK_SIZE, + z % ChunkData.CHUNK_SIZE ); //checks to see if there is only one type of voxel in this chunk @@ -233,201 +233,201 @@ public class DrawCell { * @return true if successfully filled in data, false otherwise */ private boolean fillInFaceData(TransvoxelChunkData chunkData, DrawCellFace higherLODFace, int lod){ - int mainSpacing = (int)Math.pow(2,lod); - int higherResSpacing = (int)Math.pow(2,lod - 1); - float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; - int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; - //allocate face array - for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){ - for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){ - int worldCoordOffset1 = (x * higherResSpacing) / ChunkData.CHUNK_SIZE; - int worldCoordOffset2 = (y * higherResSpacing) / ChunkData.CHUNK_SIZE; - //solve coordinates relative to the face - int localCoord1 = (x * higherResSpacing) % ChunkData.CHUNK_SIZE; - int localCoord2 = (y * higherResSpacing) % ChunkData.CHUNK_SIZE; + // int mainSpacing = (int)Math.pow(2,lod); + // int higherResSpacing = (int)Math.pow(2,lod - 1); + // float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; + // int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; + // //allocate face array + // for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){ + // for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){ + // int worldCoordOffset1 = (x * higherResSpacing) / ChunkData.CHUNK_SIZE; + // int worldCoordOffset2 = (y * higherResSpacing) / ChunkData.CHUNK_SIZE; + // //solve coordinates relative to the face + // int localCoord1 = (x * higherResSpacing) % ChunkData.CHUNK_SIZE; + // int localCoord2 = (y * higherResSpacing) % ChunkData.CHUNK_SIZE; - //implicitly performing transforms to adapt from face-space to world & local space - switch(higherLODFace){ - case X_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - worldPos.y + worldCoordOffset1, - worldPos.z + worldCoordOffset2 - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - 0, - localCoord1, - localCoord2 - ); - faceTypes[x][y] = currentChunk.getType( - 0, - localCoord1, - localCoord2 - ); - } break; - case X_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x, - worldPos.y + worldCoordOffset1, - worldPos.z + worldCoordOffset2 - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - 0, - localCoord1, - localCoord2 - ); - faceTypes[x][y] = currentChunk.getType( - 0, - localCoord1, - localCoord2 - ); - } break; - case Y_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - worldPos.z + worldCoordOffset2 - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - localCoord1, - 0, - localCoord2 - ); - faceTypes[x][y] = currentChunk.getType( - localCoord1, - 0, - localCoord2 - ); - } break; - case Y_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y, - worldPos.z + worldCoordOffset2 - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - localCoord1, - 0, - localCoord2 - ); - faceTypes[x][y] = currentChunk.getType( - localCoord1, - 0, - localCoord2 - ); - } break; - case Z_POSITIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + worldCoordOffset2, - worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - localCoord1, - localCoord2, - 0 - ); - faceTypes[x][y] = currentChunk.getType( - localCoord1, - localCoord2, - 0 - ); - } break; - case Z_NEGATIVE: { - ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - new Vector3i( - worldPos.x + worldCoordOffset1, - worldPos.y + worldCoordOffset2, - worldPos.z - ), - ChunkData.NO_STRIDE - ); - if(currentChunk == null){ - return false; - } - faceWeights[x][y] = currentChunk.getWeight( - localCoord1, - localCoord2, - 0 - ); - faceTypes[x][y] = currentChunk.getType( - localCoord1, - localCoord2, - 0 - ); - } break; - } - // Vector3i sampleChunkWorldPos = new Vector3i( - // worldPos.x + (x * higherResSpacing) / ChunkData.CHUNK_SIZE, - // worldPos.y + (y * higherResSpacing) / ChunkData.CHUNK_SIZE, - // worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE - // ); - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(sampleChunkWorldPos); - // if(currentChunk == null){ - // throw new Error("Chunk is null! " + worldPos); - // } - // weights[x][y][z] = currentChunk.getWeight( - // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, - // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, - // (z * spacingFactor) % ChunkData.CHUNK_SIZE - // ); - // types[x][y][z] = currentChunk.getType( - // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, - // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, - // (z * spacingFactor) % ChunkData.CHUNK_SIZE - // ); - } - } - switch(higherLODFace){ - case X_POSITIVE: { - chunkData.addXPositiveEdge(faceWeights, faceTypes); - } break; - case X_NEGATIVE: { - chunkData.addXNegativeEdge(faceWeights, faceTypes); - } break; - case Y_POSITIVE: { - chunkData.addYPositiveEdge(faceWeights, faceTypes); - } break; - case Y_NEGATIVE: { - chunkData.addYNegativeEdge(faceWeights, faceTypes); - } break; - case Z_POSITIVE: { - chunkData.addZPositiveEdge(faceWeights, faceTypes); - } break; - case Z_NEGATIVE: { - chunkData.addZNegativeEdge(faceWeights, faceTypes); - } break; - } + // //implicitly performing transforms to adapt from face-space to world & local space + // switch(higherLODFace){ + // case X_POSITIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, + // worldPos.y + worldCoordOffset1, + // worldPos.z + worldCoordOffset2 + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // 0, + // localCoord1, + // localCoord2 + // ); + // faceTypes[x][y] = currentChunk.getType( + // 0, + // localCoord1, + // localCoord2 + // ); + // } break; + // case X_NEGATIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x, + // worldPos.y + worldCoordOffset1, + // worldPos.z + worldCoordOffset2 + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // 0, + // localCoord1, + // localCoord2 + // ); + // faceTypes[x][y] = currentChunk.getType( + // 0, + // localCoord1, + // localCoord2 + // ); + // } break; + // case Y_POSITIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x + worldCoordOffset1, + // worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, + // worldPos.z + worldCoordOffset2 + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // localCoord1, + // 0, + // localCoord2 + // ); + // faceTypes[x][y] = currentChunk.getType( + // localCoord1, + // 0, + // localCoord2 + // ); + // } break; + // case Y_NEGATIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x + worldCoordOffset1, + // worldPos.y, + // worldPos.z + worldCoordOffset2 + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // localCoord1, + // 0, + // localCoord2 + // ); + // faceTypes[x][y] = currentChunk.getType( + // localCoord1, + // 0, + // localCoord2 + // ); + // } break; + // case Z_POSITIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x + worldCoordOffset1, + // worldPos.y + worldCoordOffset2, + // worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // localCoord1, + // localCoord2, + // 0 + // ); + // faceTypes[x][y] = currentChunk.getType( + // localCoord1, + // localCoord2, + // 0 + // ); + // } break; + // case Z_NEGATIVE: { + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + // new Vector3i( + // worldPos.x + worldCoordOffset1, + // worldPos.y + worldCoordOffset2, + // worldPos.z + // ), + // ChunkData.NO_STRIDE + // ); + // if(currentChunk == null){ + // return false; + // } + // faceWeights[x][y] = currentChunk.getWeight( + // localCoord1, + // localCoord2, + // 0 + // ); + // faceTypes[x][y] = currentChunk.getType( + // localCoord1, + // localCoord2, + // 0 + // ); + // } break; + // } + // // Vector3i sampleChunkWorldPos = new Vector3i( + // // worldPos.x + (x * higherResSpacing) / ChunkData.CHUNK_SIZE, + // // worldPos.y + (y * higherResSpacing) / ChunkData.CHUNK_SIZE, + // // worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE + // // ); + // // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(sampleChunkWorldPos); + // // if(currentChunk == null){ + // // throw new Error("Chunk is null! " + worldPos); + // // } + // // weights[x][y][z] = currentChunk.getWeight( + // // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, + // // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, + // // (z * spacingFactor) % ChunkData.CHUNK_SIZE + // // ); + // // types[x][y][z] = currentChunk.getType( + // // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, + // // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, + // // (z * spacingFactor) % ChunkData.CHUNK_SIZE + // // ); + // } + // } + // switch(higherLODFace){ + // case X_POSITIVE: { + // chunkData.addXPositiveEdge(faceWeights, faceTypes); + // } break; + // case X_NEGATIVE: { + // chunkData.addXNegativeEdge(faceWeights, faceTypes); + // } break; + // case Y_POSITIVE: { + // chunkData.addYPositiveEdge(faceWeights, faceTypes); + // } break; + // case Y_NEGATIVE: { + // chunkData.addYNegativeEdge(faceWeights, faceTypes); + // } break; + // case Z_POSITIVE: { + // chunkData.addZPositiveEdge(faceWeights, faceTypes); + // } break; + // case Z_NEGATIVE: { + // chunkData.addZNegativeEdge(faceWeights, faceTypes); + // } break; + // } return true; } From 6b2f323fdf049139f9f6ff2d5c007677cdd74546 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 5 Nov 2024 15:50:25 -0500 Subject: [PATCH 08/21] lod cache stratification working --- .../client/terrain/cells/ClientDrawCellManager.java | 7 +++---- .../java/electrosphere/client/terrain/cells/DrawCell.java | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index fee5ce7f..8db77ca5 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -6,7 +6,6 @@ import java.util.List; import org.joml.Vector3d; import org.joml.Vector3i; -import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cells.DrawCell.DrawCellFace; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; @@ -510,11 +509,11 @@ public class ClientDrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, lod) ){ //client should request chunk data from server for each chunk necessary to create the model LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); - Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE); + Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, lod); } } } @@ -542,7 +541,7 @@ public class ClientDrawCellManager { posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && posToCheck.z >= 0 && posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, lod) ){ return false; } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 81c63096..d80f7f91 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -12,7 +12,6 @@ import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.types.terrain.TerrainChunk; -import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; /** @@ -195,7 +194,7 @@ public class DrawCell { worldPos.x + x / ChunkData.CHUNK_SIZE * spacingFactor, worldPos.y + y / ChunkData.CHUNK_SIZE * spacingFactor, worldPos.z + z / ChunkData.CHUNK_SIZE * spacingFactor, - ChunkData.NO_STRIDE + lod ); if(currentChunk == null){ return false; From 4b8e4cb542095babbf6e52112b248d8da1666078 Mon Sep 17 00:00:00 2001 From: austin Date: Wed, 6 Nov 2024 10:05:06 -0500 Subject: [PATCH 09/21] fix server caching of terrain --- .../TestGenerationChunkGenerator.java | 36 ++++++--- .../manager/ChunkGenerationThread.java | 6 +- .../terrain/manager/ServerChunkCache.java | 79 +++++++++++++------ .../terrain/manager/ServerTerrainManager.java | 10 +-- 4 files changed, 91 insertions(+), 40 deletions(-) diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index aa299212..05c5ba93 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -105,11 +105,22 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); } + //stride value + int strideValue = (int)Math.pow(2,stride); + //presolve heightfield float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - heightfield[x][z] = heightmapGen.getHeight(this.terrainModel.getSeed(), this.serverWorldData.convertVoxelToRealSpace(x, worldX), this.serverWorldData.convertVoxelToRealSpace(z, worldZ)); + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + heightfield[x][z] = heightmapGen.getHeight( + this.terrainModel.getSeed(), + this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX), + this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ) + ); } } @@ -117,7 +128,13 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - GeneratedVoxel voxel = this.getVoxel(worldX, worldY, worldZ, x, y, z, heightfield, this.terrainModel, surfaceBiome); + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); weights[x][y][z] = voxel.weight; values[x][y][z] = voxel.type; } @@ -144,7 +161,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { * @param chunkX The chunk x pos * @param chunkY The chunk y pos * @param chunkZ The chunk z pos - * @param heightfield The precomputed heightfield + * @param surfaceHeight The height of the surface at x,z * @param terrainModel The terrain model * @param surfaceBiome The surface biome of the chunk * @return The value of the chunk @@ -152,7 +169,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { private GeneratedVoxel getVoxel( int worldX, int worldY, int worldZ, int chunkX, int chunkY, int chunkZ, - float[][] heightfield, + float surfaceHeight, TerrainModel terrainModel, BiomeData surfaceBiome ){ @@ -163,16 +180,15 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); } - double realX = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldX); + double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX); double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY); - double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldZ); + double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ); - float surfaceHeight = heightfield[chunkX][chunkZ]; double flooredSurfaceHeight = Math.floor(surfaceHeight); Globals.profiler.endCpuSample(); if(realY < surfaceHeight - 1){ return getSubsurfaceVoxel( - worldX,worldY, worldZ, + worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, surfaceHeight, flooredSurfaceHeight, @@ -181,7 +197,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { ); } else if(realY > flooredSurfaceHeight) { return getOverSurfaceVoxel( - worldX,worldY, worldZ, + worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, surfaceHeight, flooredSurfaceHeight, @@ -190,7 +206,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { ); } else { return getSurfaceVoxel( - worldX,worldY, worldZ, + worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, surfaceHeight, flooredSurfaceHeight, diff --git a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java index 5446f56e..816036b6 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java @@ -96,8 +96,8 @@ public class ChunkGenerationThread implements Runnable { ServerTerrainChunk chunk = null; int i = 0; while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){ - if(chunkCache.containsChunk(worldX,worldY,worldZ)){ - chunk = chunkCache.get(worldX,worldY,worldZ); + if(chunkCache.containsChunk(worldX,worldY,worldZ,stride)){ + chunk = chunkCache.get(worldX, worldY, worldZ, stride); } else { //pull from disk if it exists if(chunkDiskMap != null){ @@ -110,7 +110,7 @@ public class ChunkGenerationThread implements Runnable { chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, stride); } if(chunk != null){ - chunkCache.add(worldX, worldY, worldZ, chunk); + chunkCache.add(worldX, worldY, worldZ, stride, chunk); } } if(chunk == null){ diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index 3d8fbc9b..188a5d37 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -6,8 +6,11 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; +import io.github.studiorailgun.HashUtils; + /** * Caches chunk data on the server */ @@ -16,7 +19,7 @@ public class ServerChunkCache { /** * Number of chunks to cache */ - static final int CACHE_SIZE = 5000; + static final int CACHE_SIZE = 2500; /** * The size of the cache @@ -24,19 +27,24 @@ public class ServerChunkCache { int cacheSize = CACHE_SIZE; /** - * The cached data + * The map of full res chunk key -> chunk data */ - Map chunkCache = new HashMap(); + Map cacheMapFullRes = new ConcurrentHashMap(); + + /** + * The map of half res chunk key -> chunk data + */ + Map cacheMapHalfRes = new ConcurrentHashMap(); /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) */ - List queryRecencyQueue = new LinkedList(); + List queryRecencyQueue = new LinkedList(); /** * Tracks what chunks are already queued to be asynchronously loaded. Used so we don't have two threads generating/fetching the same chunk */ - Map queuedChunkMap = new HashMap(); + Map queuedChunkMap = new HashMap(); /** * The lock for thread safety @@ -49,7 +57,7 @@ public class ServerChunkCache { */ public Collection getContents(){ lock.acquireUninterruptibly(); - Collection rVal = Collections.unmodifiableCollection(chunkCache.values()); + Collection rVal = Collections.unmodifiableCollection(cacheMapFullRes.values()); lock.release(); return rVal; } @@ -59,7 +67,8 @@ public class ServerChunkCache { */ public void clear(){ lock.acquireUninterruptibly(); - chunkCache.clear(); + cacheMapFullRes.clear(); + cacheMapHalfRes.clear(); lock.release(); } @@ -68,15 +77,17 @@ public class ServerChunkCache { * @param worldX The world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate + * @param stride The stride of the data * @return The chunk */ - public ServerTerrainChunk get(int worldX, int worldY, int worldZ){ + public ServerTerrainChunk get(int worldX, int worldY, int worldZ, int stride){ ServerTerrainChunk rVal = null; - String key = this.getKey(worldX, worldY, worldZ); + Long key = this.getKey(worldX, worldY, worldZ); lock.acquireUninterruptibly(); queryRecencyQueue.remove(key); queryRecencyQueue.add(0, key); - rVal = this.chunkCache.get(key); + Map cache = this.getCache(stride); + rVal = cache.get(key); lock.release(); return rVal; } @@ -86,13 +97,15 @@ public class ServerChunkCache { * @param worldX The world x coordinate of the chunk * @param worldY The world y coordinate of the chunk * @param worldZ The world z coordinate of the chunk + * @param stride The stride of the data * @param chunk The chunk itself */ - public void add(int worldX, int worldY, int worldZ, ServerTerrainChunk chunk){ - String key = this.getKey(worldX, worldY, worldZ); + public void add(int worldX, int worldY, int worldZ, int stride, ServerTerrainChunk chunk){ + Long key = this.getKey(worldX, worldY, worldZ); lock.acquireUninterruptibly(); queryRecencyQueue.add(0, key); - this.chunkCache.put(key, chunk); + Map cache = this.getCache(stride); + cache.put(key, chunk); lock.release(); } @@ -101,12 +114,14 @@ public class ServerChunkCache { * @param worldX The world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate + * @param stride The stride of the data * @return true if the cache contains this chunk, false otherwise */ - public boolean containsChunk(int worldX, int worldY, int worldZ){ - String key = this.getKey(worldX,worldY,worldZ); + public boolean containsChunk(int worldX, int worldY, int worldZ, int stride){ + Long key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); - boolean rVal = this.chunkCache.containsKey(key); + Map cache = this.getCache(stride); + boolean rVal = cache.containsKey(key); lock.release(); return rVal; } @@ -118,8 +133,8 @@ public class ServerChunkCache { * @param worldZ The z component * @return The key */ - public String getKey(int worldX, int worldY, int worldZ){ - return worldX + "_" + worldY + "_" + worldZ; + public long getKey(int worldX, int worldY, int worldZ){ + return HashUtils.cantorHash(worldX, worldY, worldZ); } /** @@ -130,7 +145,7 @@ public class ServerChunkCache { * @return true if the chunk is already queued, false otherwise */ public boolean chunkIsQueued(int worldX, int worldY, int worldZ){ - String key = this.getKey(worldX,worldY,worldZ); + Long key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); boolean rVal = this.queuedChunkMap.containsKey(key); lock.release(); @@ -144,7 +159,7 @@ public class ServerChunkCache { * @param worldZ The world z position of the chunk */ public void queueChunk(int worldX, int worldY, int worldZ){ - String key = this.getKey(worldX,worldY,worldZ); + Long key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); this.queuedChunkMap.put(key,true); lock.release(); @@ -155,12 +170,32 @@ public class ServerChunkCache { * @param worldX The world x position of the chunk * @param worldY The world y position of the chunk * @param worldZ The world z position of the chunk + * @param stride The stride of the chunk */ - public void unqueueChunk(int worldX, int worldY, int worldZ){ - String key = this.getKey(worldX,worldY,worldZ); + public void unqueueChunk(int worldX, int worldY, int worldZ, int stride){ + Long key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); this.queuedChunkMap.remove(key); lock.release(); } + + /** + * Gets the cache + * @param stride The stride of the data + * @return The cache to use + */ + public Map getCache(int stride){ + switch(stride){ + case 0: { + return cacheMapFullRes; + } + case 1: { + return cacheMapHalfRes; + } + default: { + throw new Error("Invalid stride probided! " + stride); + } + } + } } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index 3a3ad4b9..e022d83b 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -232,8 +232,8 @@ public class ServerTerrainManager { Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk"); //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING ServerTerrainChunk returnedChunk = null; - if(chunkCache.containsChunk(worldX,worldY,worldZ)){ - returnedChunk = chunkCache.get(worldX,worldY,worldZ); + if(chunkCache.containsChunk(worldX,worldY,worldZ,ChunkData.NO_STRIDE)){ + returnedChunk = chunkCache.get(worldX,worldY,worldZ, ChunkData.NO_STRIDE); } else { //pull from disk if it exists if(chunkDiskMap != null){ @@ -245,7 +245,7 @@ public class ServerTerrainManager { if(returnedChunk == null){ returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, ChunkData.NO_STRIDE); } - this.chunkCache.add(worldX, worldY, worldZ, returnedChunk); + this.chunkCache.add(worldX, worldY, worldZ, ChunkData.NO_STRIDE, returnedChunk); } Globals.profiler.endCpuSample(); return returnedChunk; @@ -289,8 +289,8 @@ public class ServerTerrainManager { if(model != null){ model.addModification(modification); } - if(chunkCache.containsChunk(worldPos.x,worldPos.y,worldPos.z)){ - ServerTerrainChunk chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z); + if(chunkCache.containsChunk(worldPos.x,worldPos.y,worldPos.z,ChunkData.NO_STRIDE)){ + ServerTerrainChunk chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z, ChunkData.NO_STRIDE); chunk.addModification(modification); } } From c045c0a0d40f5ef69a302ea261afda71447d9981 Mon Sep 17 00:00:00 2001 From: austin Date: Wed, 6 Nov 2024 14:18:47 -0500 Subject: [PATCH 10/21] Strided terrain data --- .../terrain/cells/ClientDrawCellManager.java | 86 ++-- .../client/terrain/cells/DrawCell.java | 391 +++++++++--------- .../meshgen/TransvoxelModelGeneration.java | 4 +- 3 files changed, 243 insertions(+), 238 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 8db77ca5..311ab8a5 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -548,48 +548,50 @@ public class ClientDrawCellManager { } } } - // if(highResFaces != null){ - // for(DrawCellFace highResFace : highResFaces){ - // //x & y are in face-space - // for(int x = 0; x < 4; x++){ - // for(int y = 0; y < 4; y++){ - // Vector3i posToCheck = null; - // //implicitly performing transforms to adapt from face-space to world space - // switch(highResFace){ - // case X_POSITIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(lodMultiplitier,x,y); - // } break; - // case X_NEGATIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(-1,x,y); - // } break; - // case Y_POSITIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(x,lodMultiplitier,y); - // } break; - // case Y_NEGATIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(x,-1,y); - // } break; - // case Z_POSITIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,lodMultiplitier); - // } break; - // case Z_NEGATIVE: { - // posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,-1); - // } break; - // } - // if( - // posToCheck.x >= 0 && - // posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && - // posToCheck.y >= 0 && - // posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && - // posToCheck.z >= 0 && - // posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && - // !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, ChunkData.NO_STRIDE) - // ){ - // return false; - // } - // } - // } - // } - // } + int highResLod = this.chunkTree.getMaxLevel() - (node.getLevel() + 1); + int highResSpacingFactor = (int)Math.pow(2,highResLod); + if(highResFaces != null){ + for(DrawCellFace highResFace : highResFaces){ + //x & y are in face-space + for(int x = 0; x < 3; x++){ + for(int y = 0; y < 3; y++){ + Vector3i posToCheck = null; + //implicitly performing transforms to adapt from face-space to world space + switch(highResFace){ + case X_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(spacingFactor,x*highResSpacingFactor,y*highResSpacingFactor); + } break; + case X_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(0,x*highResSpacingFactor,y*highResSpacingFactor); + } break; + case Y_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,spacingFactor,y*highResSpacingFactor); + } break; + case Y_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,0,y*highResSpacingFactor); + } break; + case Z_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,spacingFactor); + } break; + case Z_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,0); + } break; + } + if( + posToCheck.x >= 0 && + posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.y >= 0 && + posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.z >= 0 && + posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod) + ){ + return false; + } + } + } + } + } return true; } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index d80f7f91..9e09ccbc 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -12,7 +12,9 @@ import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.types.terrain.TerrainChunk; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; +import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * A single drawcell - contains an entity that has a physics mesh and potentially graphics @@ -232,201 +234,202 @@ public class DrawCell { * @return true if successfully filled in data, false otherwise */ private boolean fillInFaceData(TransvoxelChunkData chunkData, DrawCellFace higherLODFace, int lod){ - // int mainSpacing = (int)Math.pow(2,lod); - // int higherResSpacing = (int)Math.pow(2,lod - 1); - // float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; - // int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; - // //allocate face array - // for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){ - // for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){ - // int worldCoordOffset1 = (x * higherResSpacing) / ChunkData.CHUNK_SIZE; - // int worldCoordOffset2 = (y * higherResSpacing) / ChunkData.CHUNK_SIZE; - // //solve coordinates relative to the face - // int localCoord1 = (x * higherResSpacing) % ChunkData.CHUNK_SIZE; - // int localCoord2 = (y * higherResSpacing) % ChunkData.CHUNK_SIZE; + int mainSpacing = (int)Math.pow(2,lod); + int higherLOD = lod - 1; + int higherResSpacing = (int)Math.pow(2,higherLOD); + float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; + int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS]; + //allocate face array + for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){ + for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){ + int worldCoordOffset1 = x / ChunkData.CHUNK_SIZE * higherResSpacing; + int worldCoordOffset2 = y / ChunkData.CHUNK_SIZE * higherResSpacing; + //solve coordinates relative to the face + int localCoord1 = x % ChunkData.CHUNK_SIZE; + int localCoord2 = y % ChunkData.CHUNK_SIZE; - // //implicitly performing transforms to adapt from face-space to world & local space - // switch(higherLODFace){ - // case X_POSITIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - // worldPos.y + worldCoordOffset1, - // worldPos.z + worldCoordOffset2 - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // 0, - // localCoord1, - // localCoord2 - // ); - // faceTypes[x][y] = currentChunk.getType( - // 0, - // localCoord1, - // localCoord2 - // ); - // } break; - // case X_NEGATIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x, - // worldPos.y + worldCoordOffset1, - // worldPos.z + worldCoordOffset2 - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // 0, - // localCoord1, - // localCoord2 - // ); - // faceTypes[x][y] = currentChunk.getType( - // 0, - // localCoord1, - // localCoord2 - // ); - // } break; - // case Y_POSITIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x + worldCoordOffset1, - // worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE, - // worldPos.z + worldCoordOffset2 - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // localCoord1, - // 0, - // localCoord2 - // ); - // faceTypes[x][y] = currentChunk.getType( - // localCoord1, - // 0, - // localCoord2 - // ); - // } break; - // case Y_NEGATIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x + worldCoordOffset1, - // worldPos.y, - // worldPos.z + worldCoordOffset2 - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // localCoord1, - // 0, - // localCoord2 - // ); - // faceTypes[x][y] = currentChunk.getType( - // localCoord1, - // 0, - // localCoord2 - // ); - // } break; - // case Z_POSITIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x + worldCoordOffset1, - // worldPos.y + worldCoordOffset2, - // worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // localCoord1, - // localCoord2, - // 0 - // ); - // faceTypes[x][y] = currentChunk.getType( - // localCoord1, - // localCoord2, - // 0 - // ); - // } break; - // case Z_NEGATIVE: { - // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - // new Vector3i( - // worldPos.x + worldCoordOffset1, - // worldPos.y + worldCoordOffset2, - // worldPos.z - // ), - // ChunkData.NO_STRIDE - // ); - // if(currentChunk == null){ - // return false; - // } - // faceWeights[x][y] = currentChunk.getWeight( - // localCoord1, - // localCoord2, - // 0 - // ); - // faceTypes[x][y] = currentChunk.getType( - // localCoord1, - // localCoord2, - // 0 - // ); - // } break; - // } - // // Vector3i sampleChunkWorldPos = new Vector3i( - // // worldPos.x + (x * higherResSpacing) / ChunkData.CHUNK_SIZE, - // // worldPos.y + (y * higherResSpacing) / ChunkData.CHUNK_SIZE, - // // worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE - // // ); - // // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(sampleChunkWorldPos); - // // if(currentChunk == null){ - // // throw new Error("Chunk is null! " + worldPos); - // // } - // // weights[x][y][z] = currentChunk.getWeight( - // // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, - // // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, - // // (z * spacingFactor) % ChunkData.CHUNK_SIZE - // // ); - // // types[x][y][z] = currentChunk.getType( - // // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, - // // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, - // // (z * spacingFactor) % ChunkData.CHUNK_SIZE - // // ); - // } - // } - // switch(higherLODFace){ - // case X_POSITIVE: { - // chunkData.addXPositiveEdge(faceWeights, faceTypes); - // } break; - // case X_NEGATIVE: { - // chunkData.addXNegativeEdge(faceWeights, faceTypes); - // } break; - // case Y_POSITIVE: { - // chunkData.addYPositiveEdge(faceWeights, faceTypes); - // } break; - // case Y_NEGATIVE: { - // chunkData.addYNegativeEdge(faceWeights, faceTypes); - // } break; - // case Z_POSITIVE: { - // chunkData.addZPositiveEdge(faceWeights, faceTypes); - // } break; - // case Z_NEGATIVE: { - // chunkData.addZNegativeEdge(faceWeights, faceTypes); - // } break; - // } + //implicitly performing transforms to adapt from face-space to world & local space + switch(higherLODFace){ + case X_POSITIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + mainSpacing, + worldPos.y + worldCoordOffset1, + worldPos.z + worldCoordOffset2 + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + 0, + localCoord1, + localCoord2 + ); + faceTypes[x][y] = currentChunk.getType( + 0, + localCoord1, + localCoord2 + ); + } break; + case X_NEGATIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x, + worldPos.y + worldCoordOffset1, + worldPos.z + worldCoordOffset2 + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + 0, + localCoord1, + localCoord2 + ); + faceTypes[x][y] = currentChunk.getType( + 0, + localCoord1, + localCoord2 + ); + } break; + case Y_POSITIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + mainSpacing, + worldPos.z + worldCoordOffset2 + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + localCoord1, + 0, + localCoord2 + ); + faceTypes[x][y] = currentChunk.getType( + localCoord1, + 0, + localCoord2 + ); + } break; + case Y_NEGATIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y, + worldPos.z + worldCoordOffset2 + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + localCoord1, + 0, + localCoord2 + ); + faceTypes[x][y] = currentChunk.getType( + localCoord1, + 0, + localCoord2 + ); + } break; + case Z_POSITIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + worldCoordOffset2, + worldPos.z + mainSpacing + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + localCoord1, + localCoord2, + 0 + ); + faceTypes[x][y] = currentChunk.getType( + localCoord1, + localCoord2, + 0 + ); + } break; + case Z_NEGATIVE: { + ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( + new Vector3i( + worldPos.x + worldCoordOffset1, + worldPos.y + worldCoordOffset2, + worldPos.z + ), + higherLOD + ); + if(currentChunk == null){ + return false; + } + faceWeights[x][y] = currentChunk.getWeight( + localCoord1, + localCoord2, + 0 + ); + faceTypes[x][y] = currentChunk.getType( + localCoord1, + localCoord2, + 0 + ); + } break; + } + // Vector3i sampleChunkWorldPos = new Vector3i( + // worldPos.x + (x * higherResSpacing) / ChunkData.CHUNK_SIZE, + // worldPos.y + (y * higherResSpacing) / ChunkData.CHUNK_SIZE, + // worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE + // ); + // ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(sampleChunkWorldPos); + // if(currentChunk == null){ + // throw new Error("Chunk is null! " + worldPos); + // } + // weights[x][y][z] = currentChunk.getWeight( + // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, + // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, + // (z * spacingFactor) % ChunkData.CHUNK_SIZE + // ); + // types[x][y][z] = currentChunk.getType( + // (x * higherResSpacing) % ChunkData.CHUNK_SIZE, + // (y * higherResSpacing) % ChunkData.CHUNK_SIZE, + // (z * spacingFactor) % ChunkData.CHUNK_SIZE + // ); + } + } + switch(higherLODFace){ + case X_POSITIVE: { + chunkData.addXPositiveEdge(faceWeights, faceTypes); + } break; + case X_NEGATIVE: { + chunkData.addXNegativeEdge(faceWeights, faceTypes); + } break; + case Y_POSITIVE: { + chunkData.addYPositiveEdge(faceWeights, faceTypes); + } break; + case Y_NEGATIVE: { + chunkData.addYNegativeEdge(faceWeights, faceTypes); + } break; + case Z_POSITIVE: { + chunkData.addZPositiveEdge(faceWeights, faceTypes); + } break; + case Z_NEGATIVE: { + chunkData.addZNegativeEdge(faceWeights, faceTypes); + } break; + } return true; } diff --git a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java index 76dd24b1..8622a1ff 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java @@ -1335,7 +1335,7 @@ public class TransvoxelModelGeneration { chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0] ); - polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert, false); + polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert, true); // //Generate the normal cell with half width @@ -1349,7 +1349,7 @@ public class TransvoxelModelGeneration { chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0] ); //polygonize the current gridcell - polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert, false); + polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert, true); } } } else { From d077f43188deba3da25c2629e7d0aaedbfc9aec2 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 09:17:46 -0500 Subject: [PATCH 11/21] Add quarter res chunks + good requeue mechanism --- .../terrain/cache/ClientTerrainCache.java | 8 ++ .../terrain/cells/ClientDrawCellManager.java | 91 ++++++++++++++++++- .../client/terrain/cells/DrawCell.java | 27 +++++- .../terrain/manager/ServerChunkCache.java | 8 ++ 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index a8d2509a..addfd30f 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -31,6 +31,11 @@ public class ClientTerrainCache { */ Map cacheMapHalfRes = new ConcurrentHashMap(); + /** + * The map of half res chunk key -> chunk data + */ + Map cacheMapQuarterRes = new ConcurrentHashMap(); + /** * The list of keys in the cache */ @@ -146,6 +151,9 @@ public class ClientTerrainCache { case 1: { return cacheMapHalfRes; } + case 2: { + return cacheMapQuarterRes; + } default: { throw new Error("Invalid stride probided! " + stride); } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 311ab8a5..e0b00e96 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -25,6 +25,11 @@ public class ClientDrawCellManager { */ static final int UPDATE_ATTEMPTS_PER_FRAME = 3; + /** + * The number of generation attempts before a cell is marked as having not requested its data + */ + static final int FAILED_GENERATION_ATTEMPT_THRESHOLD = 250; + /** * The distance to draw at full resolution */ @@ -35,6 +40,11 @@ public class ClientDrawCellManager { */ public static final double HALF_RES_DIST = 16 * ServerTerrainChunk.CHUNK_DIMENSION; + /** + * The distance for quarter resolution + */ + public static final double QUARTER_RES_DIST = 20 * ServerTerrainChunk.CHUNK_DIMENSION; + /** * The octree holding all the chunks to evaluate */ @@ -164,7 +174,8 @@ public class ClientDrawCellManager { } else if(shouldRequest(playerPos, node)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.request"); DrawCell cell = node.getData(); - this.requestChunks(node); + List highResFaces = this.solveHighResFace(node); + this.requestChunks(node, highResFaces); cell.setHasRequested(true); Globals.profiler.endCpuSample(); updated = true; @@ -174,6 +185,11 @@ public class ClientDrawCellManager { List highResFaces = this.solveHighResFace(node); if(containsDataToGenerate(node,highResFaces)){ node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces); + } else if(node.getData() != null){ + node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1); + if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){ + node.getData().setHasRequested(false); + } } Globals.profiler.endCpuSample(); updated = true; @@ -215,6 +231,11 @@ public class ClientDrawCellManager { node.isLeaf() && node.canSplit() && ( + ( + node.getLevel() < this.chunkTree.getMaxLevel() - 2 && + this.getMinDistance(pos, node) <= QUARTER_RES_DIST + ) + || ( node.getLevel() < this.chunkTree.getMaxLevel() - 1 && this.getMinDistance(pos, node) <= HALF_RES_DIST @@ -358,10 +379,15 @@ public class ClientDrawCellManager { node.getLevel() == this.chunkTree.getMaxLevel() - 1 && this.getMinDistance(pos, node) > FULL_RES_DIST ) - || + || ( + node.getLevel() == this.chunkTree.getMaxLevel() - 2 && this.getMinDistance(pos, node) > HALF_RES_DIST ) + || + ( + this.getMinDistance(pos, node) > QUARTER_RES_DIST + ) ) ; } @@ -389,6 +415,12 @@ public class ClientDrawCellManager { && this.getMinDistance(pos, node) <= HALF_RES_DIST ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - 2 + && + this.getMinDistance(pos, node) <= QUARTER_RES_DIST + ) ) ; } @@ -410,12 +442,18 @@ public class ClientDrawCellManager { // && // this.getMinDistance(pos, node) <= FULL_RES_DIST ) - || + || ( node.getLevel() == this.chunkTree.getMaxLevel() - 1 && this.getMinDistance(pos, node) <= HALF_RES_DIST ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - 2 + && + this.getMinDistance(pos, node) <= QUARTER_RES_DIST + ) ) ; } @@ -494,7 +532,7 @@ public class ClientDrawCellManager { * Requests all chunks for a given draw cell * @param cell The cell */ - private void requestChunks(WorldOctTree.FloatingChunkTreeNode node){ + private void requestChunks(WorldOctTree.FloatingChunkTreeNode node, List highResFaces){ DrawCell cell = node.getData(); int lod = this.chunkTree.getMaxLevel() - node.getLevel(); int spacingFactor = (int)Math.pow(2,lod); @@ -518,6 +556,51 @@ public class ClientDrawCellManager { } } } + int highResLod = this.chunkTree.getMaxLevel() - (node.getLevel() + 1); + int highResSpacingFactor = (int)Math.pow(2,highResLod); + if(highResFaces != null){ + for(DrawCellFace highResFace : highResFaces){ + //x & y are in face-space + for(int x = 0; x < 3; x++){ + for(int y = 0; y < 3; y++){ + Vector3i posToCheck = null; + //implicitly performing transforms to adapt from face-space to world space + switch(highResFace){ + case X_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(spacingFactor,x*highResSpacingFactor,y*highResSpacingFactor); + } break; + case X_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(0,x*highResSpacingFactor,y*highResSpacingFactor); + } break; + case Y_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,spacingFactor,y*highResSpacingFactor); + } break; + case Y_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,0,y*highResSpacingFactor); + } break; + case Z_POSITIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,spacingFactor); + } break; + case Z_NEGATIVE: { + posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,0); + } break; + } + if( + posToCheck.x >= 0 && + posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.y >= 0 && + posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.z >= 0 && + posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod) + ){ + LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); + Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod); + } + } + } + } + } } /** diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 9e09ccbc..239c04d8 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -14,7 +14,6 @@ import electrosphere.entity.Entity; import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; -import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * A single drawcell - contains an entity that has a physics mesh and potentially graphics @@ -88,6 +87,11 @@ public class DrawCell { * True if there are multiple types of voxels in this chunk */ boolean multiVoxelType = false; + + /** + * Number of failed generation attempts + */ + int failedGenerationAttempts = 0; /** @@ -118,6 +122,7 @@ public class DrawCell { Globals.profiler.endCpuSample(); if(!success){ this.setHasRequested(false); + this.failedGenerationAttempts++; return; } if(modelEntity != null){ @@ -131,6 +136,7 @@ public class DrawCell { Globals.profiler.endCpuSample(); if(!success){ this.setHasRequested(false); + this.failedGenerationAttempts++; return; } } @@ -464,6 +470,9 @@ public class DrawCell { */ public void setHasRequested(boolean hasRequested) { this.hasRequested = hasRequested; + if(!this.hasRequested){ + this.failedGenerationAttempts = 0; + } } /** @@ -490,6 +499,22 @@ public class DrawCell { return this.multiVoxelType; } + /** + * Gets the number of failed generation attempts + * @return The number of failed generation attempts + */ + public int getFailedGenerationAttempts(){ + return failedGenerationAttempts; + } + + /** + * Sets the number of failed generation attempts + * @param attempts The number of failed generation attempts + */ + public void setFailedGenerationAttempts(int attempts){ + failedGenerationAttempts = failedGenerationAttempts + 1; + } + } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index 188a5d37..6700cac7 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -36,6 +36,11 @@ public class ServerChunkCache { */ Map cacheMapHalfRes = new ConcurrentHashMap(); + /** + * The map of quarter res chunk key -> chunk data + */ + Map cacheMapQuarterRes = new ConcurrentHashMap(); + /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) */ @@ -192,6 +197,9 @@ public class ServerChunkCache { case 1: { return cacheMapHalfRes; } + case 2: { + return cacheMapQuarterRes; + } default: { throw new Error("Invalid stride probided! " + stride); } From 249e80d4ae6268572066238f2f8044c2bb98614a Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 09:23:35 -0500 Subject: [PATCH 12/21] fix midrange chunk visual gen --- .../client/terrain/cells/ClientDrawCellManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index e0b00e96..ee30cc3e 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -413,7 +413,7 @@ public class ClientDrawCellManager { ( node.getLevel() == this.chunkTree.getMaxLevel() - 1 && - this.getMinDistance(pos, node) <= HALF_RES_DIST + this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) || ( @@ -446,7 +446,7 @@ public class ClientDrawCellManager { ( node.getLevel() == this.chunkTree.getMaxLevel() - 1 && - this.getMinDistance(pos, node) <= HALF_RES_DIST + this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) || ( From 7493c1128bde787b76711a8e5cadc46148df5ff8 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 10:04:51 -0500 Subject: [PATCH 13/21] eighth res chunk gen --- .../terrain/cache/ClientTerrainCache.java | 10 +++- .../terrain/cells/ClientDrawCellManager.java | 36 +++++++++++-- .../client/terrain/cells/DrawCell.java | 51 ++++++++++++------- .../terrain/manager/ServerChunkCache.java | 8 +++ 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index addfd30f..21ffcda9 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -32,10 +32,15 @@ public class ClientTerrainCache { Map cacheMapHalfRes = new ConcurrentHashMap(); /** - * The map of half res chunk key -> chunk data + * The map of quarter res chunk key -> chunk data */ Map cacheMapQuarterRes = new ConcurrentHashMap(); + /** + * The map of eighth res chunk key -> chunk data + */ + Map cacheMapEighthRes = new ConcurrentHashMap(); + /** * The list of keys in the cache */ @@ -154,6 +159,9 @@ public class ClientTerrainCache { case 2: { return cacheMapQuarterRes; } + case 3: { + return cacheMapEighthRes; + } default: { throw new Error("Invalid stride probided! " + stride); } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index ee30cc3e..1d9d0fcf 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -45,6 +45,11 @@ public class ClientDrawCellManager { */ public static final double QUARTER_RES_DIST = 20 * ServerTerrainChunk.CHUNK_DIMENSION; + /** + * The distance for eighth resolution + */ + public static final double EIGHTH_RES_DIST = 28 * ServerTerrainChunk.CHUNK_DIMENSION; + /** * The octree holding all the chunks to evaluate */ @@ -185,6 +190,9 @@ public class ClientDrawCellManager { List highResFaces = this.solveHighResFace(node); if(containsDataToGenerate(node,highResFaces)){ node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces); + if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){ + node.getData().setHasRequested(false); + } } else if(node.getData() != null){ node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1); if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){ @@ -231,6 +239,11 @@ public class ClientDrawCellManager { node.isLeaf() && node.canSplit() && ( + ( + node.getLevel() < this.chunkTree.getMaxLevel() - 3 && + this.getMinDistance(pos, node) <= EIGHTH_RES_DIST + ) + || ( node.getLevel() < this.chunkTree.getMaxLevel() - 2 && this.getMinDistance(pos, node) <= QUARTER_RES_DIST @@ -386,8 +399,13 @@ public class ClientDrawCellManager { ) || ( + node.getLevel() == this.chunkTree.getMaxLevel() - 3 && this.getMinDistance(pos, node) > QUARTER_RES_DIST ) + || + ( + this.getMinDistance(pos, node) > EIGHTH_RES_DIST + ) ) ; } @@ -415,11 +433,17 @@ public class ClientDrawCellManager { && this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) - || + || ( node.getLevel() == this.chunkTree.getMaxLevel() - 2 && - this.getMinDistance(pos, node) <= QUARTER_RES_DIST + this.getMinDistance(pos, node) <= EIGHTH_RES_DIST + ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - 3 + && + this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) ) ; @@ -452,7 +476,13 @@ public class ClientDrawCellManager { ( node.getLevel() == this.chunkTree.getMaxLevel() - 2 && - this.getMinDistance(pos, node) <= QUARTER_RES_DIST + this.getMinDistance(pos, node) <= EIGHTH_RES_DIST + ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - 3 + && + this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) ) ; diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 239c04d8..d46a8719 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -121,8 +121,7 @@ public class DrawCell { boolean success = this.fillInData(lod); Globals.profiler.endCpuSample(); if(!success){ - this.setHasRequested(false); - this.failedGenerationAttempts++; + this.setFailedGenerationAttempts(this.getFailedGenerationAttempts() + 1); return; } if(modelEntity != null){ @@ -135,8 +134,7 @@ public class DrawCell { success = this.fillInFaceData(chunkData,face,lod); Globals.profiler.endCpuSample(); if(!success){ - this.setHasRequested(false); - this.failedGenerationAttempts++; + this.setFailedGenerationAttempts(this.getFailedGenerationAttempts() + 1); return; } } @@ -199,24 +197,39 @@ public class DrawCell { for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){ for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){ ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( - worldPos.x + x / ChunkData.CHUNK_SIZE * spacingFactor, - worldPos.y + y / ChunkData.CHUNK_SIZE * spacingFactor, - worldPos.z + z / ChunkData.CHUNK_SIZE * spacingFactor, + worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor, + worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor, + worldPos.z + (z / ChunkData.CHUNK_SIZE) * spacingFactor, lod ); if(currentChunk == null){ - return false; + Vector3i posToCheck = new Vector3i( + worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor, + worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor, + worldPos.z + (z / ChunkData.CHUNK_SIZE) * spacingFactor + ); + if( + posToCheck.x >= 0 && + posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.y >= 0 && + posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() && + posToCheck.z >= 0 && + posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() + ){ + return false; + } + } else { + weights[x][y][z] = currentChunk.getWeight( + x % ChunkData.CHUNK_SIZE, + y % ChunkData.CHUNK_SIZE, + z % ChunkData.CHUNK_SIZE + ); + types[x][y][z] = currentChunk.getType( + x % ChunkData.CHUNK_SIZE, + y % ChunkData.CHUNK_SIZE, + z % ChunkData.CHUNK_SIZE + ); } - weights[x][y][z] = currentChunk.getWeight( - x % ChunkData.CHUNK_SIZE, - y % ChunkData.CHUNK_SIZE, - z % ChunkData.CHUNK_SIZE - ); - types[x][y][z] = currentChunk.getType( - x % ChunkData.CHUNK_SIZE, - y % ChunkData.CHUNK_SIZE, - z % ChunkData.CHUNK_SIZE - ); //checks to see if there is only one type of voxel in this chunk if(!this.multiVoxelType){ @@ -512,7 +525,7 @@ public class DrawCell { * @param attempts The number of failed generation attempts */ public void setFailedGenerationAttempts(int attempts){ - failedGenerationAttempts = failedGenerationAttempts + 1; + this.failedGenerationAttempts = this.failedGenerationAttempts + attempts; } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index 6700cac7..633e49a7 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -41,6 +41,11 @@ public class ServerChunkCache { */ Map cacheMapQuarterRes = new ConcurrentHashMap(); + /** + * The map of eighth res chunk key -> chunk data + */ + Map cacheMapEighthRes = new ConcurrentHashMap(); + /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) */ @@ -200,6 +205,9 @@ public class ServerChunkCache { case 2: { return cacheMapQuarterRes; } + case 3: { + return cacheMapEighthRes; + } default: { throw new Error("Invalid stride probided! " + stride); } From e6fdac489c2f6e17a94ca027c8ae999729cbe85a Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 11:44:51 -0500 Subject: [PATCH 14/21] stratified updates to client cell manager --- .../terrain/cells/ClientDrawCellManager.java | 71 ++++++++++++++++--- .../engine/loadingthreads/ClientLoading.java | 6 +- .../TestGenerationChunkGenerator.java | 2 +- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 1d9d0fcf..9ee876be 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -50,6 +50,26 @@ public class ClientDrawCellManager { */ public static final double EIGHTH_RES_DIST = 28 * ServerTerrainChunk.CHUNK_DIMENSION; + /** + * Lod value for a full res chunk + */ + public static final int FULL_RES_LOD = 0; + + /** + * Lod value for a half res chunk + */ + public static final int HALF_RES_LOD = 1; + + /** + * Lod value for a quarter res chunk + */ + public static final int QUARTER_RES_LOD = 2; + + /** + * Lod value for a eighth res chunk + */ + public static final int EIGHTH_RES_LOD = 3; + /** * The octree holding all the chunks to evaluate */ @@ -95,6 +115,11 @@ public class ClientDrawCellManager { */ int generated = 0; + /** + * Tracks whether the cell manager has initialized or not + */ + boolean initialized = false; + /** * Constructor * @param voxelTextureAtlas The voxel texture atlas @@ -115,12 +140,18 @@ public class ClientDrawCellManager { Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); //the sets to iterate through updatedLastFrame = true; - int attempts = 0; validCellCount = 0; - while(updatedLastFrame && attempts < UPDATE_ATTEMPTS_PER_FRAME){ - FloatingChunkTreeNode rootNode = this.chunkTree.getRoot(); - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos); - attempts++; + //update all full res cells + FloatingChunkTreeNode rootNode = this.chunkTree.getRoot(); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, HALF_RES_LOD); + if(!updatedLastFrame && !this.initialized){ + this.initialized = true; + } + if(!updatedLastFrame){ + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, QUARTER_RES_LOD); + } + if(!updatedLastFrame){ + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, EIGHTH_RES_LOD); } } Globals.profiler.endCpuSample(); @@ -130,9 +161,10 @@ public class ClientDrawCellManager { * Recursively update child nodes * @param node The root node * @param playerPos The player's position + * @param minLeafLod The minimum LOD required to evaluate a leaf * @return true if there is work remaining to be done, false otherwise */ - private boolean recursivelyUpdateCells(FloatingChunkTreeNode node, Vector3d playerPos){ + private boolean recursivelyUpdateCells(FloatingChunkTreeNode node, Vector3d playerPos, int minLeafLod){ Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity); boolean updated = false; if(this.shouldSplit(playerPos, node)){ @@ -176,15 +208,20 @@ public class ClientDrawCellManager { Globals.profiler.endCpuSample(); updated = true; - } else if(shouldRequest(playerPos, node)){ + } else if(shouldRequest(playerPos, node, minLeafLod)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.request"); + + //calculate what to request DrawCell cell = node.getData(); List highResFaces = this.solveHighResFace(node); + + //actually send requests this.requestChunks(node, highResFaces); cell.setHasRequested(true); + Globals.profiler.endCpuSample(); updated = true; - } else if(shouldGenerate(playerPos, node)){ + } else if(shouldGenerate(playerPos, node, minLeafLod)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.generate"); int lodLevel = this.getLODLevel(playerRealPos, node); List highResFaces = this.solveHighResFace(node); @@ -205,7 +242,7 @@ public class ClientDrawCellManager { this.validCellCount++; List> children = new LinkedList>(node.getChildren()); for(FloatingChunkTreeNode child : children){ - boolean childUpdate = recursivelyUpdateCells(child, playerPos); + boolean childUpdate = recursivelyUpdateCells(child, playerPos, minLeafLod); if(childUpdate == true){ updated = true; } @@ -414,13 +451,15 @@ public class ClientDrawCellManager { * Checks if this cell should request chunk data * @param pos the player's position * @param node the node + * @param minLeafLod The minimum LOD required to evaluate a leaf * @return true if should request chunk data, false otherwise */ - public boolean shouldRequest(Vector3d pos, FloatingChunkTreeNode node){ + public boolean shouldRequest(Vector3d pos, FloatingChunkTreeNode node, int minLeafLod){ return node.isLeaf() && node.getData() != null && !node.getData().hasRequested() && + (this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod && ( ( node.getLevel() == this.chunkTree.getMaxLevel() @@ -453,13 +492,15 @@ public class ClientDrawCellManager { * Checks if this cell should generate * @param pos the player's position * @param node the node + * @param minLeafLod The minimum LOD required to evaluate a leaf * @return true if should generate, false otherwise */ - public boolean shouldGenerate(Vector3d pos, FloatingChunkTreeNode node){ + public boolean shouldGenerate(Vector3d pos, FloatingChunkTreeNode node, int minLeafLod){ return node.isLeaf() && node.getData() != null && !node.getData().hasGenerated() && + (this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod && ( ( node.getLevel() == this.chunkTree.getMaxLevel() @@ -788,6 +829,14 @@ public class ClientDrawCellManager { return generated; } + /** + * Gets whether the client draw cell manager has initialized or not + * @return true if it has initialized, false otherwise + */ + public boolean isInitialized(){ + return this.initialized; + } + } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 80fca928..09ad0d56 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -297,7 +297,11 @@ public class ClientLoading { Globals.clientSimulation.setLoadingTerrain(true); //wait for all the terrain data to arrive int i = 0; - while(blockForInit && Globals.clientDrawCellManager.updatedLastFrame() && Globals.threadManager.shouldKeepRunning()){ + while( + blockForInit && + !Globals.clientDrawCellManager.isInitialized() && + Globals.threadManager.shouldKeepRunning() + ){ i++; if(i % DRAW_CELL_UPDATE_RATE == 0){ WindowUtils.updateLoadingWindow("WAITING ON SERVER TO SEND TERRAIN (" + Globals.clientTerrainManager.getAllChunks().size() + ")"); diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 05c5ba93..de7bf5c4 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -24,7 +24,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { /** * The size of the realm for testing generation */ - public static final int GENERATOR_REALM_SIZE = 32; + public static final int GENERATOR_REALM_SIZE = 64; /** * The default biome index From f7cc0b4dcf3562c462b1940117498dabcd108e2d Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 11:55:56 -0500 Subject: [PATCH 15/21] add mechanism to deduplicate outgoing requests --- .../terrain/cache/ClientTerrainCache.java | 20 ++++++++- .../terrain/cells/ClientDrawCellManager.java | 12 +++-- .../terrain/manager/ClientTerrainManager.java | 45 ++++++++++++++++--- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index 21ffcda9..0536d585 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Semaphore; import org.joml.Vector3i; @@ -50,6 +51,11 @@ public class ClientTerrainCache { * A map of chunk to its world position */ Map chunkPositionMap = new ConcurrentHashMap(); + + /** + * The lock on the terrain cache + */ + Semaphore lock = new Semaphore(1); /** * Constructor @@ -67,6 +73,7 @@ public class ClientTerrainCache { * @param chunkData The chunk data to add at the specified positions */ public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){ + lock.acquireUninterruptibly(); Map cache = this.getCache(chunkData.getStride()); cache.put(getKey(worldX,worldY,worldZ),chunkData); chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ)); @@ -74,16 +81,19 @@ public class ClientTerrainCache { Long currentChunk = cacheList.remove(0); cache.remove(currentChunk); } + lock.release(); } /** * Evicts all chunks from the cache */ public void evictAll(){ + lock.acquireUninterruptibly(); this.cacheList.clear(); this.cacheMapFullRes.clear(); this.cacheMapHalfRes.clear(); this.chunkPositionMap.clear(); + lock.release(); } @@ -107,7 +117,10 @@ public class ClientTerrainCache { * @return True if the cache contains chunk data at the specified point, false otherwise */ public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){ - return this.getCache(stride).containsKey(getKey(worldX,worldY,worldZ)); + lock.acquireUninterruptibly(); + boolean rVal = this.getCache(stride).containsKey(getKey(worldX,worldY,worldZ)); + lock.release(); + return rVal; } @@ -123,7 +136,10 @@ public class ClientTerrainCache { * @return The chunk data if it exists, null otherwise */ public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ, int stride){ - return this.getCache(stride).get(getKey(worldX,worldY,worldZ)); + lock.acquireUninterruptibly(); + ChunkData rVal = this.getCache(stride).get(getKey(worldX,worldY,worldZ)); + lock.release(); + return rVal; } /** diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 9ee876be..10f93fb2 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -602,8 +602,9 @@ public class ClientDrawCellManager { /** * Requests all chunks for a given draw cell * @param cell The cell + * @return true if all cells were successfully requested, false otherwise */ - private void requestChunks(WorldOctTree.FloatingChunkTreeNode node, List highResFaces){ + private boolean requestChunks(WorldOctTree.FloatingChunkTreeNode node, List highResFaces){ DrawCell cell = node.getData(); int lod = this.chunkTree.getMaxLevel() - node.getLevel(); int spacingFactor = (int)Math.pow(2,lod); @@ -622,7 +623,9 @@ public class ClientDrawCellManager { ){ //client should request chunk data from server for each chunk necessary to create the model LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); - Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, lod); + if(!Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, lod)){ + return false; + } } } } @@ -666,12 +669,15 @@ public class ClientDrawCellManager { !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod) ){ LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck); - Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod); + if(!Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod)){ + return false; + } } } } } } + return true; } /** diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 8bdd02a7..5a3af2c1 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -6,7 +6,9 @@ import java.nio.IntBuffer; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Semaphore; @@ -25,6 +27,7 @@ import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; import electrosphere.renderer.model.Model; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; +import io.github.studiorailgun.HashUtils; /** * Manages terrain storage and access on the client @@ -58,6 +61,11 @@ public class ClientTerrainManager { //The queue of terrain chunk data to be buffered to gpu static List terrainChunkGenerationQueue = new CopyOnWriteArrayList(); + + /** + * Tracks what outgoing requests are currently active + */ + Map requestedMap = new ConcurrentHashMap(); /** * Constructor @@ -134,6 +142,8 @@ public class ClientTerrainManager { message.getworldX(), message.getworldY(), message.getworldZ(), data ); + //remove from request map + this.requestedMap.remove(this.getRequestKey(message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution())); } break; default: LoggerInterface.loggerEngine.WARNING("ClientTerrainManager: unhandled network message of type" + message.getMessageSubtype()); @@ -192,14 +202,23 @@ public class ClientTerrainManager { * @param worldY the world y coordinate of the chunk * @param worldZ the world z coordinate of the chunk * @param stride The stride of the data + * @return true if the request was successfully sent, false otherwise */ - public void requestChunk(int worldX, int worldY, int worldZ, int stride){ - Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( - worldX, - worldY, - worldZ, - stride - )); + public boolean requestChunk(int worldX, int worldY, int worldZ, int stride){ + boolean rVal = false; + lock.acquireUninterruptibly(); + if(!this.requestedMap.containsKey(this.getRequestKey(worldX, worldY, worldZ, stride))){ + Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( + worldX, + worldY, + worldZ, + stride + )); + this.requestedMap.put(this.getRequestKey(worldX, worldY, worldZ, stride), true); + rVal = true; + } + lock.release(); + return rVal; } /** @@ -273,5 +292,17 @@ public class ClientTerrainManager { public Vector3i getPositionOfChunk(ChunkData chunk){ return terrainCache.getChunkPosition(chunk); } + + /** + * Gets the key for a given request + * @param worldX The world x coordinate + * @param worldY The world y coordinate + * @param worldZ The world z coordinate + * @param stride The stride of the data + * @return The key + */ + private Long getRequestKey(int worldX, int worldY, int worldZ, int stride){ + return (long)HashUtils.cantorHash(worldY, worldZ, worldZ); + } } From 59b899b1d732997caccf1611d89718b93229e15b Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 12:08:00 -0500 Subject: [PATCH 16/21] support for sixteenth resolution chunks --- .../terrain/cache/ClientTerrainCache.java | 8 +++ .../terrain/cells/ClientDrawCellManager.java | 69 +++++++++++++++---- .../terrain/manager/ClientTerrainManager.java | 7 +- .../terrain/manager/ServerChunkCache.java | 8 +++ 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index 0536d585..589f0952 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -42,6 +42,11 @@ public class ClientTerrainCache { */ Map cacheMapEighthRes = new ConcurrentHashMap(); + /** + * The map of sixteenth res chunk key -> chunk data + */ + Map cacheMapSixteenthRes = new ConcurrentHashMap(); + /** * The list of keys in the cache */ @@ -178,6 +183,9 @@ public class ClientTerrainCache { case 3: { return cacheMapEighthRes; } + case 4: { + return cacheMapEighthRes; + } default: { throw new Error("Invalid stride probided! " + stride); } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 10f93fb2..d09385c8 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -48,7 +48,12 @@ public class ClientDrawCellManager { /** * The distance for eighth resolution */ - public static final double EIGHTH_RES_DIST = 28 * ServerTerrainChunk.CHUNK_DIMENSION; + public static final double EIGHTH_RES_DIST = 32 * ServerTerrainChunk.CHUNK_DIMENSION; + + /** + * The distance for sixteenth resolution + */ + public static final double SIXTEENTH_RES_DIST = 48 * ServerTerrainChunk.CHUNK_DIMENSION; /** * Lod value for a full res chunk @@ -70,6 +75,16 @@ public class ClientDrawCellManager { */ public static final int EIGHTH_RES_LOD = 3; + /** + * Lod value for a sixteenth res chunk + */ + public static final int SIXTEENTH_RES_LOD = 4; + + /** + * Lod value for evaluating all lod levels + */ + public static final int ALL_RES_LOD = 5; + /** * The octree holding all the chunks to evaluate */ @@ -153,6 +168,12 @@ public class ClientDrawCellManager { if(!updatedLastFrame){ updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, EIGHTH_RES_LOD); } + if(!updatedLastFrame){ + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, SIXTEENTH_RES_LOD); + } + if(!updatedLastFrame){ + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, ALL_RES_LOD); + } } Globals.profiler.endCpuSample(); } @@ -277,17 +298,22 @@ public class ClientDrawCellManager { node.canSplit() && ( ( - node.getLevel() < this.chunkTree.getMaxLevel() - 3 && + node.getLevel() < this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && + this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST + ) + || + ( + node.getLevel() < this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) || ( - node.getLevel() < this.chunkTree.getMaxLevel() - 2 && + node.getLevel() < this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) || ( - node.getLevel() < this.chunkTree.getMaxLevel() - 1 && + node.getLevel() < this.chunkTree.getMaxLevel() - HALF_RES_LOD && this.getMinDistance(pos, node) <= HALF_RES_DIST ) || @@ -426,23 +452,28 @@ public class ClientDrawCellManager { !node.isLeaf() && ( ( - node.getLevel() == this.chunkTree.getMaxLevel() - 1 && + node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD && this.getMinDistance(pos, node) > FULL_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 2 && + node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && this.getMinDistance(pos, node) > HALF_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 3 && + node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && this.getMinDistance(pos, node) > QUARTER_RES_DIST ) || ( + node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && this.getMinDistance(pos, node) > EIGHTH_RES_DIST ) + || + ( + this.getMinDistance(pos, node) > SIXTEENTH_RES_DIST + ) ) ; } @@ -468,22 +499,28 @@ public class ClientDrawCellManager { ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 1 + node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD && this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 2 + node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 3 + node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD + && + this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST + ) ) ; } @@ -509,22 +546,28 @@ public class ClientDrawCellManager { ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 1 + node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD && this.getMinDistance(pos, node) <= QUARTER_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 2 + node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) || ( - node.getLevel() == this.chunkTree.getMaxLevel() - 3 + node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && this.getMinDistance(pos, node) <= EIGHTH_RES_DIST ) + || + ( + node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD + && + this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST + ) ) ; } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 5a3af2c1..f253cef0 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -41,6 +41,11 @@ public class ClientTerrainManager { * Locks the terrain manager (eg if adding message from network) */ static Semaphore lock = new Semaphore(1); + + /** + * Maximum concurrent terrain requests + */ + public static final int MAX_CONCURRENT_REQUESTS = 500; //The interpolation ratio of terrain public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; @@ -207,7 +212,7 @@ public class ClientTerrainManager { public boolean requestChunk(int worldX, int worldY, int worldZ, int stride){ boolean rVal = false; lock.acquireUninterruptibly(); - if(!this.requestedMap.containsKey(this.getRequestKey(worldX, worldY, worldZ, stride))){ + if(this.requestedMap.size() < MAX_CONCURRENT_REQUESTS && !this.requestedMap.containsKey(this.getRequestKey(worldX, worldY, worldZ, stride))){ Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage( worldX, worldY, diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index 633e49a7..da685ea8 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -46,6 +46,11 @@ public class ServerChunkCache { */ Map cacheMapEighthRes = new ConcurrentHashMap(); + /** + * The map of sixteenth res chunk key -> chunk data + */ + Map cacheMapSixteenthRes = new ConcurrentHashMap(); + /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) */ @@ -208,6 +213,9 @@ public class ServerChunkCache { case 3: { return cacheMapEighthRes; } + case 4: { + return cacheMapSixteenthRes; + } default: { throw new Error("Invalid stride probided! " + stride); } From ae4c0aa4e54bc4a3c29952f27e584ad4dd31e3de Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 12:25:31 -0500 Subject: [PATCH 17/21] add request timeout mechanism --- .../terrain/cells/ClientDrawCellManager.java | 7 ++++--- .../terrain/manager/ClientTerrainManager.java | 20 ++++++++++++++++--- .../TestGenerationChunkGenerator.java | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index d09385c8..b84dd2c6 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -53,7 +53,7 @@ public class ClientDrawCellManager { /** * The distance for sixteenth resolution */ - public static final double SIXTEENTH_RES_DIST = 48 * ServerTerrainChunk.CHUNK_DIMENSION; + public static final double SIXTEENTH_RES_DIST = 128 * ServerTerrainChunk.CHUNK_DIMENSION; /** * Lod value for a full res chunk @@ -237,8 +237,9 @@ public class ClientDrawCellManager { List highResFaces = this.solveHighResFace(node); //actually send requests - this.requestChunks(node, highResFaces); - cell.setHasRequested(true); + if(this.requestChunks(node, highResFaces)){ + cell.setHasRequested(true); + } Globals.profiler.endCpuSample(); updated = true; diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index f253cef0..a8e35170 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -45,7 +45,12 @@ public class ClientTerrainManager { /** * Maximum concurrent terrain requests */ - public static final int MAX_CONCURRENT_REQUESTS = 500; + public static final int MAX_CONCURRENT_REQUESTS = 500; + + /** + * Number of frames to wait before flagging a request as failed + */ + public static final int FAILED_REQUEST_THRESHOLD = 500; //The interpolation ratio of terrain public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; @@ -70,7 +75,7 @@ public class ClientTerrainManager { /** * Tracks what outgoing requests are currently active */ - Map requestedMap = new ConcurrentHashMap(); + Map requestedMap = new ConcurrentHashMap(); /** * Constructor @@ -158,6 +163,15 @@ public class ClientTerrainManager { for(TerrainMessage message : bouncedMessages){ messageQueue.add(message); } + //evaluate if any chunks have failed to request + for(Long key : this.requestedMap.keySet()){ + int duration = this.requestedMap.get(key); + if(duration > FAILED_REQUEST_THRESHOLD){ + this.requestedMap.remove(key); + } else { + this.requestedMap.put(key,duration + 1); + } + } lock.release(); Globals.profiler.endCpuSample(); } @@ -219,7 +233,7 @@ public class ClientTerrainManager { worldZ, stride )); - this.requestedMap.put(this.getRequestKey(worldX, worldY, worldZ, stride), true); + this.requestedMap.put(this.getRequestKey(worldX, worldY, worldZ, stride), 0); rVal = true; } lock.release(); diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index de7bf5c4..5b65606e 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -24,7 +24,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { /** * The size of the realm for testing generation */ - public static final int GENERATOR_REALM_SIZE = 64; + public static final int GENERATOR_REALM_SIZE = 512; /** * The default biome index From 96b65dba465e4c92e65148569ab87513d4d437a5 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 13:39:47 -0500 Subject: [PATCH 18/21] fix array error --- .../java/electrosphere/logger/Logger.java | 11 ++ .../TestGenerationChunkGenerator.java | 127 +++++++++--------- .../manager/ChunkGenerationThread.java | 53 ++++---- .../server/terrain/models/TerrainModel.java | 12 +- 4 files changed, 110 insertions(+), 93 deletions(-) diff --git a/src/main/java/electrosphere/logger/Logger.java b/src/main/java/electrosphere/logger/Logger.java index 30f8ccc0..f51b5f6f 100644 --- a/src/main/java/electrosphere/logger/Logger.java +++ b/src/main/java/electrosphere/logger/Logger.java @@ -130,6 +130,17 @@ public class Logger { } } + /** + * Logs an error message. + * This should be used every time we throw any kind of error in the engine + * @param e The exception to report + */ + public void ERROR(Error e){ + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING || level == LogLevel.ERROR){ + e.printStackTrace(); + } + } + /** * Prints a message at the specified logging level * @param level The logging level diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 5b65606e..65a86e8b 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -68,81 +68,80 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk"); ServerTerrainChunk rVal = null; - float[][][] weights; - int[][][] values; + float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];; + int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - if(worldX == 0 || worldZ == 0){ - //generate flat ground for the player to spawn on - weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - Arrays.fill(weights[x][y],-1f); + try { + if(worldX == 0 || worldZ == 0){ + //generate flat ground for the player to spawn on + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + Arrays.fill(weights[x][y],-1f); + } } - } - if(worldY == 0){ + if(worldY == 0){ + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + values[x][0][z] = 1; + weights[x][0][z] = 0.1f; + } + } + } + + + + } else { + //actual generation algo + + //biome of the current chunk + BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ); + + BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); + HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); + if(heightmapGen == null){ + throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); + } + + //stride value + int strideValue = (int)Math.pow(2,stride); + + //presolve heightfield + float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - values[x][0][z] = 1; - weights[x][0][z] = 0.1f; - } - } - } - - - - } else { - //actual generation algo - weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - - //biome of the current chunk - BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ); - - BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); - HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); - if(heightmapGen == null){ - throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); - } - - //stride value - int strideValue = (int)Math.pow(2,stride); - - //presolve heightfield - float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - heightfield[x][z] = heightmapGen.getHeight( - this.terrainModel.getSeed(), - this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX), - this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ) - ); - } - } - - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); - weights[x][y][z] = voxel.weight; - values[x][y][z] = voxel.type; + heightfield[x][z] = heightmapGen.getHeight( + this.terrainModel.getSeed(), + this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX), + this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ) + ); } } - Globals.profiler.endCpuSample(); - } - } + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); + weights[x][y][z] = voxel.weight; + values[x][y][z] = voxel.type; + } + } + Globals.profiler.endCpuSample(); + } + } + } catch(Exception ex){ + ex.printStackTrace(); + } rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); Globals.profiler.endCpuSample(); return rVal; diff --git a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java index 816036b6..cf1ed51b 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java @@ -4,6 +4,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import electrosphere.engine.Globals; +import electrosphere.logger.LoggerInterface; import electrosphere.server.terrain.diskmap.ChunkDiskMap; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; @@ -95,37 +96,41 @@ public class ChunkGenerationThread implements Runnable { public void run() { ServerTerrainChunk chunk = null; int i = 0; - while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){ - if(chunkCache.containsChunk(worldX,worldY,worldZ,stride)){ - chunk = chunkCache.get(worldX, worldY, worldZ, stride); - } else { - //pull from disk if it exists - if(chunkDiskMap != null){ - if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){ - chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ); + try { + while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){ + if(chunkCache.containsChunk(worldX,worldY,worldZ,stride)){ + chunk = chunkCache.get(worldX, worldY, worldZ, stride); + } else { + //pull from disk if it exists + if(chunkDiskMap != null){ + if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){ + chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ); + } + } + //generate if it does not exist + if(chunk == null){ + chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, stride); + } + if(chunk != null){ + chunkCache.add(worldX, worldY, worldZ, stride, chunk); } } - //generate if it does not exist if(chunk == null){ - chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ, stride); - } - if(chunk != null){ - chunkCache.add(worldX, worldY, worldZ, stride, chunk); + try { + TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS); + } catch (InterruptedException e) { + e.printStackTrace(); + } } + i++; } - if(chunk == null){ - try { - TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if(i >= MAX_TIME_TO_WAIT){ + throw new Error("Failed to resolve chunk!"); } - i++; + this.onLoad.accept(chunk); + } catch (Error e){ + LoggerInterface.loggerEngine.ERROR(e); } - if(i >= MAX_TIME_TO_WAIT){ - throw new Error("Failed to resolve chunk!"); - } - this.onLoad.accept(chunk); } } diff --git a/src/main/java/electrosphere/server/terrain/models/TerrainModel.java b/src/main/java/electrosphere/server/terrain/models/TerrainModel.java index 7c97cb86..17f4757e 100644 --- a/src/main/java/electrosphere/server/terrain/models/TerrainModel.java +++ b/src/main/java/electrosphere/server/terrain/models/TerrainModel.java @@ -127,11 +127,13 @@ public class TerrainModel { TerrainModel rVal = new TerrainModel(); rVal.discreteArrayDimension = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE; rVal.dynamicInterpolationRatio = 1; - rVal.biome = new short[2][2]; - rVal.biome[0][0] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX; - rVal.biome[1][0] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX; - rVal.biome[0][1] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX; - rVal.biome[1][1] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX; + int macroDataImageScale = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE / DEFAULT_MACRO_DATA_SCALE + 1; + rVal.biome = new short[macroDataImageScale][macroDataImageScale]; + for(int x = 0; x < macroDataImageScale; x++){ + for(int z = 0; z < macroDataImageScale; z++){ + rVal.biome[x][z] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX; + } + } return rVal; } From 1d917279519df273de4381f02563236c06521065 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 13:53:03 -0500 Subject: [PATCH 19/21] view distance adjustments --- src/main/java/electrosphere/engine/Globals.java | 3 ++- .../electrosphere/engine/loadingthreads/ClientLoading.java | 2 +- src/main/java/electrosphere/renderer/RenderPipelineState.java | 3 ++- src/main/java/electrosphere/renderer/RenderingEngine.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 8f6a99d0..f6d1618e 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -4,6 +4,7 @@ import java.lang.management.ManagementFactory; import java.util.ArrayList; +import org.joml.Matrix4d; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -257,7 +258,7 @@ public class Globals { //matrices for drawing models public static Matrix4f viewMatrix = new Matrix4f(); - public static Matrix4f projectionMatrix; + public static Matrix4d projectionMatrix; public static Matrix4f lightDepthMatrix = new Matrix4f(); //locations for shadow map specific variables diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 09ad0d56..844c22da 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -245,7 +245,7 @@ public class ClientLoading { EntityCreationUtils.makeEntityDrawable(skybox, "Models/environment/skyboxSphere.fbx"); DrawableUtils.disableCulling(skybox); EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f)); - EntityUtils.getScale(skybox).mul(200000.0f); + EntityUtils.getScale(skybox).mul(600000.0f); Globals.assetManager.queueOverrideMeshShader("Models/environment/skyboxSphere.fbx", "Sphere", "Shaders/entities/skysphere/skysphere.vs", "Shaders/entities/skysphere/skysphere.fs"); //cloud ring pseudo skybox diff --git a/src/main/java/electrosphere/renderer/RenderPipelineState.java b/src/main/java/electrosphere/renderer/RenderPipelineState.java index caee8d89..60c0d83a 100644 --- a/src/main/java/electrosphere/renderer/RenderPipelineState.java +++ b/src/main/java/electrosphere/renderer/RenderPipelineState.java @@ -1,6 +1,7 @@ package electrosphere.renderer; import org.joml.FrustumIntersection; +import org.joml.Matrix4d; import org.joml.Matrix4f; import electrosphere.renderer.actor.instance.InstanceData; @@ -166,7 +167,7 @@ public class RenderPipelineState { * @param projectionMatrix the projection matrix * @param viewMatrix the view matrix */ - public void updateFrustumIntersection(Matrix4f projectionMatrix, Matrix4f viewMatrix){ + public void updateFrustumIntersection(Matrix4d projectionMatrix, Matrix4f viewMatrix){ Matrix4f projectionViewMatrix = new Matrix4f(); projectionViewMatrix.set(projectionMatrix); projectionViewMatrix.mul(viewMatrix); diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 5e23ecdc..3b781cf2 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -453,7 +453,7 @@ public class RenderingEngine { // // Projection and View matrix creation // - Globals.projectionMatrix = new Matrix4f(); + Globals.projectionMatrix = new Matrix4d(); Globals.viewMatrix = new Matrix4f(); verticalFOV = (float)(Globals.verticalFOV * Math.PI /180.0f); //set local aspect ratio and global aspect ratio at the same time From 22e468a6d0d38a840d1cfce1433c28039fea6be1 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 14:44:55 -0500 Subject: [PATCH 20/21] remove first chunk special gen --- .../TestGenerationChunkGenerator.java | 95 ++++++++----------- .../manager/ChunkGenerationThread.java | 2 +- 2 files changed, 38 insertions(+), 59 deletions(-) diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 65a86e8b..16d83fce 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -1,6 +1,5 @@ package electrosphere.server.terrain.generation; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -72,72 +71,52 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; try { - if(worldX == 0 || worldZ == 0){ - //generate flat ground for the player to spawn on - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - Arrays.fill(weights[x][y],-1f); - } - } - if(worldY == 0){ - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - values[x][0][z] = 1; - weights[x][0][z] = 0.1f; - } - } + //actual generation algo + + //biome of the current chunk + BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ); + + BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); + HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); + if(heightmapGen == null){ + throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); + } + + //stride value + int strideValue = (int)Math.pow(2,stride); + + //presolve heightfield + float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + heightfield[x][z] = heightmapGen.getHeight( + this.terrainModel.getSeed(), + this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX), + this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ) + ); } + } - - - } else { - //actual generation algo - - //biome of the current chunk - BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ); - - BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); - HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); - if(heightmapGen == null){ - throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); - } - - //stride value - int strideValue = (int)Math.pow(2,stride); - - //presolve heightfield - float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - heightfield[x][z] = heightmapGen.getHeight( - this.terrainModel.getSeed(), - this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX), - this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ) - ); + GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); + weights[x][y][z] = voxel.weight; + values[x][y][z] = voxel.type; } } - - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); - weights[x][y][z] = voxel.weight; - values[x][y][z] = voxel.type; - } - } - Globals.profiler.endCpuSample(); - } + Globals.profiler.endCpuSample(); } } catch(Exception ex){ ex.printStackTrace(); diff --git a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java index cf1ed51b..7d391c54 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java @@ -98,7 +98,7 @@ public class ChunkGenerationThread implements Runnable { int i = 0; try { while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){ - if(chunkCache.containsChunk(worldX,worldY,worldZ,stride)){ + if(chunkCache.containsChunk(worldX, worldY, worldZ, stride)){ chunk = chunkCache.get(worldX, worldY, worldZ, stride); } else { //pull from disk if it exists From 36e261b0545ba69d21f4b182975ae942aae0ac58 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 7 Nov 2024 14:59:25 -0500 Subject: [PATCH 21/21] terrain lod bounding sphere fix --- .../entity/types/terrain/TerrainChunkData.java | 17 ++++++++++++++++- .../meshgen/TerrainChunkModelGeneration.java | 15 ++++++++------- .../meshgen/TransvoxelModelGeneration.java | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java index e2982bed..b997e8d9 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java @@ -20,6 +20,11 @@ public class TerrainChunkData { //texture ratio vector List textureRatioVectors; //HOW MUCH of each texture in the atlas to sample + /** + * The LOD of the model + */ + int lod; + /** * Creates an object to hold data required to generate a chunk * @param vertices @@ -27,14 +32,16 @@ public class TerrainChunkData { * @param faceElements * @param uvs * @param textureSamplers + * @param lod The LOD of the model */ - public TerrainChunkData(List vertices, List normals, List faceElements, List uvs, List textureSamplers, List textureRatioVectors){ + public TerrainChunkData(List vertices, List normals, List faceElements, List uvs, List textureSamplers, List textureRatioVectors, int lod){ this.vertices = vertices; this.normals = normals; this.faceElements = faceElements; this.uvs = uvs; this.textureSamplers = textureSamplers; this.textureRatioVectors = textureRatioVectors; + this.lod = lod; } /** @@ -85,4 +92,12 @@ public class TerrainChunkData { return textureRatioVectors; } + /** + * Gets the LOD of the model + * @return The LOD + */ + public int getLOD(){ + return lod; + } + } diff --git a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java index ced73d9f..573be002 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java @@ -763,7 +763,7 @@ public class TerrainChunkModelGeneration { } //List vertices, List normals, List faceElements, List uvs - TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData); + TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData, 0); return rVal; } @@ -892,14 +892,15 @@ public class TerrainChunkModelGeneration { //bounding sphere logic + int distance = ServerTerrainChunk.CHUNK_DIMENSION / 2 * (int)Math.pow(2,data.getLOD()); mesh.updateBoundingSphere( - ServerTerrainChunk.CHUNK_DIMENSION, - ServerTerrainChunk.CHUNK_DIMENSION, - ServerTerrainChunk.CHUNK_DIMENSION, + distance, + distance, + distance, (float)Math.sqrt( - ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION + - ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION + - ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION + distance * distance + + distance * distance + + distance * distance )); diff --git a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java index 8622a1ff..65356f42 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java @@ -1694,7 +1694,7 @@ public class TransvoxelModelGeneration { } //List vertices, List normals, List faceElements, List uvs - TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData); + TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData, chunkData.levelOfDetail); return rVal; }