package electrosphere.client.terrain.manager; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import org.joml.Vector3i; import electrosphere.client.scene.ClientWorldData; import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ClientTerrainCache; import electrosphere.engine.Globals; import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; import electrosphere.renderer.model.Model; import electrosphere.server.terrain.manager.ServerTerrainManager; /** * Manages terrain storage and access on the client */ public class ClientTerrainManager { //queues messages from server List messageQueue = new CopyOnWriteArrayList(); //The interpolation ratio of terrain public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; //caches chunks from server static final int CACHE_SIZE = 50; //used for caching the macro values ClientTerrainCache terrainCache; //The world data for the client ClientWorldData clientWorldData; //The queue of terrain chunk data to be buffered to gpu static List terrainChunkGenerationQueue = new CopyOnWriteArrayList(); /** * Constructor */ public ClientTerrainManager(){ terrainCache = new ClientTerrainCache(CACHE_SIZE); } public void handleMessages(){ List bouncedMessages = new LinkedList(); for(TerrainMessage message : messageQueue){ messageQueue.remove(message); switch(message.getMessageSubtype()){ case SENDCHUNKDATA: { 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(); 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; } } for(TerrainMessage message : bouncedMessages){ messageQueue.add(message); } } public void attachTerrainMessage(TerrainMessage message){ messageQueue.add(message); } public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ); } 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 * @return The chunk data if it exists, otherwise null */ public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ); } /** * Gets the chunk data at a given world position * @param worldPos The world position as a joml vector * @return The chunk data if it exists, otherwise null */ public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos){ return terrainCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z); } /** * Queues a terrain chunk to be pushed to GPU based on chunk data * @param data The chunk data (triangles, normals, etc) * @return The model path that is promised to eventually reflect the terrain model when it makes it to gpu */ public static String queueTerrainGridGeneration(TerrainChunkData data){ String promisedHash = ""; UUID newUUID = UUID.randomUUID(); promisedHash = newUUID.toString(); TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash); terrainChunkGenerationQueue.add(queueItem); return promisedHash; } /** * Pushes all terrain data in queue to the gpu and registers the resulting models */ public static void generateTerrainChunkGeometry(){ for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){ Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData()); Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash()); } terrainChunkGenerationQueue.clear(); } /** * Gets all chunks in the terrain cache * @return The collection of all chunk data objects */ public Collection getAllChunks(){ return terrainCache.getAllChunks(); } /** * Gets the world position of a given chunk * @param chunk The chunk * @return The world position of the chunk */ public Vector3i getPositionOfChunk(ChunkData chunk){ return terrainCache.getChunkPosition(chunk); } }