voxelImprovements #5
@ -404,7 +404,7 @@ public class FluidCellManager {
|
|||||||
* @return The chunk data at the specified points
|
* @return The chunk data at the specified points
|
||||||
*/
|
*/
|
||||||
ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -205,7 +205,7 @@ public class FoliageCell {
|
|||||||
protected void generate(){
|
protected void generate(){
|
||||||
boolean shouldGenerate = false;
|
boolean shouldGenerate = false;
|
||||||
//get foliage types supported
|
//get foliage types supported
|
||||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition);
|
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition,ChunkData.NO_STRIDE);
|
||||||
if(data == null){
|
if(data == null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,9 +77,9 @@ public class FoliageChunk {
|
|||||||
*/
|
*/
|
||||||
public void initCells(){
|
public void initCells(){
|
||||||
Globals.profiler.beginCpuSample("FoliageChunk.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
|
// //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);
|
this.updateCells(true);
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ public class FoliageChunk {
|
|||||||
* @return true if contains foliage voxel, false otherwise
|
* @return true if contains foliage voxel, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean checkContainsFoliageVoxel(){
|
private boolean checkContainsFoliageVoxel(){
|
||||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos());
|
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos(),ChunkData.NO_STRIDE);
|
||||||
if(data == null){
|
if(data == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,11 @@ import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
|||||||
*/
|
*/
|
||||||
public class ChunkData {
|
public class ChunkData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No stride
|
||||||
|
*/
|
||||||
|
public static final int NO_STRIDE = 0;
|
||||||
|
|
||||||
//The size of a chunk in virtual data
|
//The size of a chunk in virtual data
|
||||||
public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION;
|
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
|
//The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk
|
||||||
@ -39,16 +44,23 @@ public class ChunkData {
|
|||||||
*/
|
*/
|
||||||
int worldZ;
|
int worldZ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stride of the data
|
||||||
|
*/
|
||||||
|
int stride;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a chunk data
|
* Creates a chunk data
|
||||||
* @param worldX The word x coordinate
|
* @param worldX The word x coordinate
|
||||||
* @param worldY The word y coordinate
|
* @param worldY The word y coordinate
|
||||||
* @param worldZ The word z 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.worldX = worldX;
|
||||||
this.worldY = worldY;
|
this.worldY = worldY;
|
||||||
this.worldZ = worldZ;
|
this.worldZ = worldZ;
|
||||||
|
this.stride = stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -211,5 +223,13 @@ public class ChunkData {
|
|||||||
return new Vector3i(worldX,worldY,worldZ);
|
return new Vector3i(worldX,worldY,worldZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the stride of the data
|
||||||
|
* @return The stride of the data
|
||||||
|
*/
|
||||||
|
public int getStride(){
|
||||||
|
return stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
import org.joml.Vector3i;
|
import org.joml.Vector3i;
|
||||||
|
|
||||||
@ -16,19 +17,50 @@ import io.github.studiorailgun.HashUtils;
|
|||||||
*/
|
*/
|
||||||
public class ClientTerrainCache {
|
public class ClientTerrainCache {
|
||||||
|
|
||||||
//cache capacity
|
/**
|
||||||
|
* Cache capacity
|
||||||
|
*/
|
||||||
int cacheSize;
|
int cacheSize;
|
||||||
//the map of chunk key -> chunk data
|
|
||||||
Map<Long,ChunkData> cacheMap = new ConcurrentHashMap<Long,ChunkData>();
|
/**
|
||||||
//the list of keys in the cache
|
* The map of full res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ChunkData> cacheMapFullRes = new ConcurrentHashMap<Long,ChunkData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of half res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ChunkData> cacheMapHalfRes = new ConcurrentHashMap<Long,ChunkData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of quarter res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ChunkData> cacheMapQuarterRes = new ConcurrentHashMap<Long,ChunkData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of eighth res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ChunkData> cacheMapEighthRes = new ConcurrentHashMap<Long,ChunkData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of sixteenth res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ChunkData> cacheMapSixteenthRes = new ConcurrentHashMap<Long,ChunkData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of keys in the cache
|
||||||
|
*/
|
||||||
List<Long> cacheList = new CopyOnWriteArrayList<Long>();
|
List<Long> cacheList = new CopyOnWriteArrayList<Long>();
|
||||||
//A map of chunk to its world position
|
|
||||||
|
/**
|
||||||
|
* A map of chunk to its world position
|
||||||
|
*/
|
||||||
Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>();
|
Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The map tracking chunks that have been requested
|
* The lock on the terrain cache
|
||||||
*/
|
*/
|
||||||
Map<Long,Boolean> requestedChunks = new ConcurrentHashMap<Long,Boolean>();
|
Semaphore lock = new Semaphore(1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -46,23 +78,27 @@ public class ClientTerrainCache {
|
|||||||
* @param chunkData The chunk data to add at the specified positions
|
* @param chunkData The chunk data to add at the specified positions
|
||||||
*/
|
*/
|
||||||
public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){
|
public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){
|
||||||
cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
|
lock.acquireUninterruptibly();
|
||||||
|
Map<Long,ChunkData> cache = this.getCache(chunkData.getStride());
|
||||||
|
cache.put(getKey(worldX,worldY,worldZ),chunkData);
|
||||||
chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
|
chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
|
||||||
while(cacheList.size() > cacheSize){
|
while(cacheList.size() > cacheSize){
|
||||||
Long currentChunk = cacheList.remove(0);
|
Long currentChunk = cacheList.remove(0);
|
||||||
ChunkData data = cacheMap.remove(currentChunk);
|
cache.remove(currentChunk);
|
||||||
Vector3i worldPos = data.getWorldPos();
|
|
||||||
requestedChunks.remove(getKey(worldPos.x,worldPos.y,worldPos.z));
|
|
||||||
}
|
}
|
||||||
|
lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evicts all chunks from the cache
|
* Evicts all chunks from the cache
|
||||||
*/
|
*/
|
||||||
public void evictAll(){
|
public void evictAll(){
|
||||||
|
lock.acquireUninterruptibly();
|
||||||
this.cacheList.clear();
|
this.cacheList.clear();
|
||||||
this.cacheMap.clear();
|
this.cacheMapFullRes.clear();
|
||||||
|
this.cacheMapHalfRes.clear();
|
||||||
this.chunkPositionMap.clear();
|
this.chunkPositionMap.clear();
|
||||||
|
lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,10 +118,14 @@ public class ClientTerrainCache {
|
|||||||
* @param worldX The x world position
|
* @param worldX The x world position
|
||||||
* @param worldY The y world position
|
* @param worldY The y world position
|
||||||
* @param worldZ The z 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
|
* @return True if the cache contains chunk data at the specified point, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){
|
||||||
return cacheMap.containsKey(getKey(worldX,worldY,worldZ));
|
lock.acquireUninterruptibly();
|
||||||
|
boolean rVal = this.getCache(stride).containsKey(getKey(worldX,worldY,worldZ));
|
||||||
|
lock.release();
|
||||||
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -97,10 +137,14 @@ public class ClientTerrainCache {
|
|||||||
* @param worldX The x world position
|
* @param worldX The x world position
|
||||||
* @param worldY The y world position
|
* @param worldY The y world position
|
||||||
* @param worldZ The z world position
|
* @param worldZ The z world position
|
||||||
|
* @param stride The stride of the data
|
||||||
* @return The chunk data if it exists, null otherwise
|
* @return The chunk data if it exists, null otherwise
|
||||||
*/
|
*/
|
||||||
public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ, int stride){
|
||||||
return cacheMap.get(getKey(worldX,worldY,worldZ));
|
lock.acquireUninterruptibly();
|
||||||
|
ChunkData rVal = this.getCache(stride).get(getKey(worldX,worldY,worldZ));
|
||||||
|
lock.release();
|
||||||
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +152,7 @@ public class ClientTerrainCache {
|
|||||||
* @return The list of all chunks in the cache
|
* @return The list of all chunks in the cache
|
||||||
*/
|
*/
|
||||||
public Collection<ChunkData> getAllChunks(){
|
public Collection<ChunkData> getAllChunks(){
|
||||||
return this.cacheMap.values();
|
return this.cacheMapFullRes.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,34 +163,33 @@ public class ClientTerrainCache {
|
|||||||
public Vector3i getChunkPosition(ChunkData chunk){
|
public Vector3i getChunkPosition(ChunkData chunk){
|
||||||
return chunkPositionMap.get(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cache
|
||||||
|
* @param stride The stride of the data
|
||||||
|
* @return The cache to use
|
||||||
|
*/
|
||||||
|
public Map<Long,ChunkData> getCache(int stride){
|
||||||
|
switch(stride){
|
||||||
|
case 0: {
|
||||||
|
return cacheMapFullRes;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
return cacheMapHalfRes;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
return cacheMapQuarterRes;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
return cacheMapEighthRes;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
return cacheMapEighthRes;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error("Invalid stride probided! " + stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,11 @@ public class ClientDrawCellManager {
|
|||||||
*/
|
*/
|
||||||
static final int UPDATE_ATTEMPTS_PER_FRAME = 3;
|
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
|
* The distance to draw at full resolution
|
||||||
*/
|
*/
|
||||||
@ -35,6 +40,51 @@ public class ClientDrawCellManager {
|
|||||||
*/
|
*/
|
||||||
public static final double HALF_RES_DIST = 16 * ServerTerrainChunk.CHUNK_DIMENSION;
|
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 distance for eighth resolution
|
||||||
|
*/
|
||||||
|
public static final double EIGHTH_RES_DIST = 32 * ServerTerrainChunk.CHUNK_DIMENSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The distance for sixteenth resolution
|
||||||
|
*/
|
||||||
|
public static final double SIXTEENTH_RES_DIST = 128 * 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* The octree holding all the chunks to evaluate
|
||||||
*/
|
*/
|
||||||
@ -80,6 +130,11 @@ public class ClientDrawCellManager {
|
|||||||
*/
|
*/
|
||||||
int generated = 0;
|
int generated = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks whether the cell manager has initialized or not
|
||||||
|
*/
|
||||||
|
boolean initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param voxelTextureAtlas The voxel texture atlas
|
* @param voxelTextureAtlas The voxel texture atlas
|
||||||
@ -100,12 +155,24 @@ public class ClientDrawCellManager {
|
|||||||
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||||
//the sets to iterate through
|
//the sets to iterate through
|
||||||
updatedLastFrame = true;
|
updatedLastFrame = true;
|
||||||
int attempts = 0;
|
|
||||||
validCellCount = 0;
|
validCellCount = 0;
|
||||||
while(updatedLastFrame && attempts < UPDATE_ATTEMPTS_PER_FRAME){
|
//update all full res cells
|
||||||
FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot();
|
FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot();
|
||||||
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos);
|
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, HALF_RES_LOD);
|
||||||
attempts++;
|
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);
|
||||||
|
}
|
||||||
|
if(!updatedLastFrame){
|
||||||
|
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, SIXTEENTH_RES_LOD);
|
||||||
|
}
|
||||||
|
if(!updatedLastFrame){
|
||||||
|
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, ALL_RES_LOD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
@ -115,9 +182,10 @@ public class ClientDrawCellManager {
|
|||||||
* Recursively update child nodes
|
* Recursively update child nodes
|
||||||
* @param node The root node
|
* @param node The root node
|
||||||
* @param playerPos The player's position
|
* @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
|
* @return true if there is work remaining to be done, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean recursivelyUpdateCells(FloatingChunkTreeNode<DrawCell> node, Vector3d playerPos){
|
private boolean recursivelyUpdateCells(FloatingChunkTreeNode<DrawCell> node, Vector3d playerPos, int minLeafLod){
|
||||||
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
|
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
if(this.shouldSplit(playerPos, node)){
|
if(this.shouldSplit(playerPos, node)){
|
||||||
@ -161,19 +229,34 @@ public class ClientDrawCellManager {
|
|||||||
|
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
updated = true;
|
updated = true;
|
||||||
} else if(shouldRequest(playerPos, node)){
|
} else if(shouldRequest(playerPos, node, minLeafLod)){
|
||||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
|
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
|
||||||
|
|
||||||
|
//calculate what to request
|
||||||
DrawCell cell = node.getData();
|
DrawCell cell = node.getData();
|
||||||
this.requestChunks(node);
|
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
|
||||||
cell.setHasRequested(true);
|
|
||||||
|
//actually send requests
|
||||||
|
if(this.requestChunks(node, highResFaces)){
|
||||||
|
cell.setHasRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
updated = true;
|
updated = true;
|
||||||
} else if(shouldGenerate(playerPos, node)){
|
} else if(shouldGenerate(playerPos, node, minLeafLod)){
|
||||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
|
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
|
||||||
int lodLevel = this.getLODLevel(playerRealPos, node);
|
int lodLevel = this.getLODLevel(playerRealPos, node);
|
||||||
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
|
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
|
||||||
if(containsDataToGenerate(node,highResFaces)){
|
if(containsDataToGenerate(node,highResFaces)){
|
||||||
node.getData().generateDrawableEntity(textureAtlas, lodLevel, 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){
|
||||||
|
node.getData().setHasRequested(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
updated = true;
|
updated = true;
|
||||||
@ -181,7 +264,7 @@ public class ClientDrawCellManager {
|
|||||||
this.validCellCount++;
|
this.validCellCount++;
|
||||||
List<FloatingChunkTreeNode<DrawCell>> children = new LinkedList<FloatingChunkTreeNode<DrawCell>>(node.getChildren());
|
List<FloatingChunkTreeNode<DrawCell>> children = new LinkedList<FloatingChunkTreeNode<DrawCell>>(node.getChildren());
|
||||||
for(FloatingChunkTreeNode<DrawCell> child : children){
|
for(FloatingChunkTreeNode<DrawCell> child : children){
|
||||||
boolean childUpdate = recursivelyUpdateCells(child, playerPos);
|
boolean childUpdate = recursivelyUpdateCells(child, playerPos, minLeafLod);
|
||||||
if(childUpdate == true){
|
if(childUpdate == true){
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
@ -216,7 +299,22 @@ public class ClientDrawCellManager {
|
|||||||
node.canSplit() &&
|
node.canSplit() &&
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
node.getLevel() < this.chunkTree.getMaxLevel() - 1 &&
|
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() - QUARTER_RES_LOD &&
|
||||||
|
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
|
||||||
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
node.getLevel() < this.chunkTree.getMaxLevel() - HALF_RES_LOD &&
|
||||||
this.getMinDistance(pos, node) <= HALF_RES_DIST
|
this.getMinDistance(pos, node) <= HALF_RES_DIST
|
||||||
)
|
)
|
||||||
||
|
||
|
||||||
@ -355,13 +453,28 @@ public class ClientDrawCellManager {
|
|||||||
!node.isLeaf() &&
|
!node.isLeaf() &&
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
node.getLevel() == this.chunkTree.getMaxLevel() - 1 &&
|
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD &&
|
||||||
this.getMinDistance(pos, node) > FULL_RES_DIST
|
this.getMinDistance(pos, node) > FULL_RES_DIST
|
||||||
)
|
)
|
||||||
||
|
||
|
||||||
(
|
(
|
||||||
|
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD &&
|
||||||
this.getMinDistance(pos, node) > HALF_RES_DIST
|
this.getMinDistance(pos, node) > HALF_RES_DIST
|
||||||
)
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
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
|
||||||
|
)
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@ -370,13 +483,15 @@ public class ClientDrawCellManager {
|
|||||||
* Checks if this cell should request chunk data
|
* Checks if this cell should request chunk data
|
||||||
* @param pos the player's position
|
* @param pos the player's position
|
||||||
* @param node the node
|
* @param node the node
|
||||||
|
* @param minLeafLod The minimum LOD required to evaluate a leaf
|
||||||
* @return true if should request chunk data, false otherwise
|
* @return true if should request chunk data, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean shouldRequest(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
|
public boolean shouldRequest(Vector3d pos, FloatingChunkTreeNode<DrawCell> node, int minLeafLod){
|
||||||
return
|
return
|
||||||
node.isLeaf() &&
|
node.isLeaf() &&
|
||||||
node.getData() != null &&
|
node.getData() != null &&
|
||||||
!node.getData().hasRequested() &&
|
!node.getData().hasRequested() &&
|
||||||
|
(this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod &&
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
node.getLevel() == this.chunkTree.getMaxLevel()
|
node.getLevel() == this.chunkTree.getMaxLevel()
|
||||||
@ -385,9 +500,27 @@ public class ClientDrawCellManager {
|
|||||||
)
|
)
|
||||||
||
|
||
|
||||||
(
|
(
|
||||||
node.getLevel() == this.chunkTree.getMaxLevel() - 1
|
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD
|
||||||
&&
|
&&
|
||||||
this.getMinDistance(pos, node) <= HALF_RES_DIST
|
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
|
||||||
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD
|
||||||
|
&&
|
||||||
|
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||||
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
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
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
@ -397,24 +530,44 @@ public class ClientDrawCellManager {
|
|||||||
* Checks if this cell should generate
|
* Checks if this cell should generate
|
||||||
* @param pos the player's position
|
* @param pos the player's position
|
||||||
* @param node the node
|
* @param node the node
|
||||||
|
* @param minLeafLod The minimum LOD required to evaluate a leaf
|
||||||
* @return true if should generate, false otherwise
|
* @return true if should generate, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean shouldGenerate(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
|
public boolean shouldGenerate(Vector3d pos, FloatingChunkTreeNode<DrawCell> node, int minLeafLod){
|
||||||
return
|
return
|
||||||
node.isLeaf() &&
|
node.isLeaf() &&
|
||||||
node.getData() != null &&
|
node.getData() != null &&
|
||||||
!node.getData().hasGenerated() &&
|
!node.getData().hasGenerated() &&
|
||||||
|
(this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod &&
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
node.getLevel() == this.chunkTree.getMaxLevel()
|
node.getLevel() == this.chunkTree.getMaxLevel()
|
||||||
// &&
|
// &&
|
||||||
// this.getMinDistance(pos, node) <= FULL_RES_DIST
|
// this.getMinDistance(pos, node) <= FULL_RES_DIST
|
||||||
)
|
)
|
||||||
||
|
||
|
||||||
(
|
(
|
||||||
node.getLevel() == this.chunkTree.getMaxLevel() - 1
|
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD
|
||||||
&&
|
&&
|
||||||
this.getMinDistance(pos, node) <= HALF_RES_DIST
|
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
|
||||||
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD
|
||||||
|
&&
|
||||||
|
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||||
|
)
|
||||||
|
||
|
||||||
|
(
|
||||||
|
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
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
@ -490,31 +643,19 @@ public class ClientDrawCellManager {
|
|||||||
throw new Error("Unimplemented");
|
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);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests all chunks for a given draw cell
|
* Requests all chunks for a given draw cell
|
||||||
* @param cell The cell
|
* @param cell The cell
|
||||||
|
* @return true if all cells were successfully requested, false otherwise
|
||||||
*/
|
*/
|
||||||
private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node){
|
private boolean requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
|
||||||
DrawCell cell = node.getData();
|
DrawCell cell = node.getData();
|
||||||
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
|
int lod = this.chunkTree.getMaxLevel() - node.getLevel();
|
||||||
for(int i = 0; i < 2 * lodMultiplitier; i++){
|
int spacingFactor = (int)Math.pow(2,lod);
|
||||||
for(int j = 0; j < 2 * lodMultiplitier; j++){
|
for(int i = 0; i < 2; i++){
|
||||||
for(int k = 0; k < 2 * lodMultiplitier; k++){
|
for(int j = 0; j < 2; j++){
|
||||||
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k);
|
for(int k = 0; k < 2; k++){
|
||||||
|
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i*spacingFactor,j*spacingFactor,k*spacingFactor);
|
||||||
if(
|
if(
|
||||||
posToCheck.x >= 0 &&
|
posToCheck.x >= 0 &&
|
||||||
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
@ -522,15 +663,65 @@ public class ClientDrawCellManager {
|
|||||||
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
posToCheck.z >= 0 &&
|
posToCheck.z >= 0 &&
|
||||||
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
|
!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
|
//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);
|
LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck);
|
||||||
Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z);
|
if(!Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, lod)){
|
||||||
|
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)
|
||||||
|
){
|
||||||
|
LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck);
|
||||||
|
if(!Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -541,11 +732,12 @@ public class ClientDrawCellManager {
|
|||||||
*/
|
*/
|
||||||
private boolean containsDataToGenerate(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
|
private boolean containsDataToGenerate(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
|
||||||
DrawCell cell = node.getData();
|
DrawCell cell = node.getData();
|
||||||
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
|
int lod = this.chunkTree.getMaxLevel() - node.getLevel();
|
||||||
for(int i = 0; i < 2 * lodMultiplitier; i++){
|
int spacingFactor = (int)Math.pow(2,lod);
|
||||||
for(int j = 0; j < 2 * lodMultiplitier; j++){
|
for(int i = 0; i < 2; i++){
|
||||||
for(int k = 0; k < 2 * lodMultiplitier; k++){
|
for(int j = 0; j < 2; j++){
|
||||||
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k);
|
for(int k = 0; k < 2; k++){
|
||||||
|
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i*spacingFactor,j*spacingFactor,k*spacingFactor);
|
||||||
if(
|
if(
|
||||||
posToCheck.x >= 0 &&
|
posToCheck.x >= 0 &&
|
||||||
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
@ -553,38 +745,40 @@ public class ClientDrawCellManager {
|
|||||||
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
posToCheck.z >= 0 &&
|
posToCheck.z >= 0 &&
|
||||||
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
|
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, lod)
|
||||||
){
|
){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int highResLod = this.chunkTree.getMaxLevel() - (node.getLevel() + 1);
|
||||||
|
int highResSpacingFactor = (int)Math.pow(2,highResLod);
|
||||||
if(highResFaces != null){
|
if(highResFaces != null){
|
||||||
for(DrawCellFace highResFace : highResFaces){
|
for(DrawCellFace highResFace : highResFaces){
|
||||||
//x & y are in face-space
|
//x & y are in face-space
|
||||||
for(int x = 0; x < 2 * lodMultiplitier; x++){
|
for(int x = 0; x < 3; x++){
|
||||||
for(int y = 0; y < 2 * lodMultiplitier; y++){
|
for(int y = 0; y < 3; y++){
|
||||||
Vector3i posToCheck = null;
|
Vector3i posToCheck = null;
|
||||||
//implicitly performing transforms to adapt from face-space to world space
|
//implicitly performing transforms to adapt from face-space to world space
|
||||||
switch(highResFace){
|
switch(highResFace){
|
||||||
case X_POSITIVE: {
|
case X_POSITIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(lodMultiplitier,x,y);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(spacingFactor,x*highResSpacingFactor,y*highResSpacingFactor);
|
||||||
} break;
|
} break;
|
||||||
case X_NEGATIVE: {
|
case X_NEGATIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(-1,x,y);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(0,x*highResSpacingFactor,y*highResSpacingFactor);
|
||||||
} break;
|
} break;
|
||||||
case Y_POSITIVE: {
|
case Y_POSITIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(x,lodMultiplitier,y);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,spacingFactor,y*highResSpacingFactor);
|
||||||
} break;
|
} break;
|
||||||
case Y_NEGATIVE: {
|
case Y_NEGATIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(x,-1,y);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,0,y*highResSpacingFactor);
|
||||||
} break;
|
} break;
|
||||||
case Z_POSITIVE: {
|
case Z_POSITIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,lodMultiplitier);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,spacingFactor);
|
||||||
} break;
|
} break;
|
||||||
case Z_NEGATIVE: {
|
case Z_NEGATIVE: {
|
||||||
posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,-1);
|
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,0);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
if(
|
if(
|
||||||
@ -594,7 +788,7 @@ public class ClientDrawCellManager {
|
|||||||
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
posToCheck.z >= 0 &&
|
posToCheck.z >= 0 &&
|
||||||
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
|
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod)
|
||||||
){
|
){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -685,6 +879,14 @@ public class ClientDrawCellManager {
|
|||||||
return generated;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,11 @@ public class DrawCell {
|
|||||||
* True if there are multiple types of voxels in this chunk
|
* True if there are multiple types of voxels in this chunk
|
||||||
*/
|
*/
|
||||||
boolean multiVoxelType = false;
|
boolean multiVoxelType = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of failed generation attempts
|
||||||
|
*/
|
||||||
|
int failedGenerationAttempts = 0;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +121,7 @@ public class DrawCell {
|
|||||||
boolean success = this.fillInData(lod);
|
boolean success = this.fillInData(lod);
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
if(!success){
|
if(!success){
|
||||||
this.setHasRequested(false);
|
this.setFailedGenerationAttempts(this.getFailedGenerationAttempts() + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(modelEntity != null){
|
if(modelEntity != null){
|
||||||
@ -129,7 +134,7 @@ public class DrawCell {
|
|||||||
success = this.fillInFaceData(chunkData,face,lod);
|
success = this.fillInFaceData(chunkData,face,lod);
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
if(!success){
|
if(!success){
|
||||||
this.setHasRequested(false);
|
this.setFailedGenerationAttempts(this.getFailedGenerationAttempts() + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,23 +197,39 @@ public class DrawCell {
|
|||||||
for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
|
for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
|
||||||
for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + (x * spacingFactor) / ChunkData.CHUNK_SIZE,
|
worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor,
|
||||||
worldPos.y + (y * spacingFactor) / ChunkData.CHUNK_SIZE,
|
worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor,
|
||||||
worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE
|
worldPos.z + (z / ChunkData.CHUNK_SIZE) * spacingFactor,
|
||||||
|
lod
|
||||||
);
|
);
|
||||||
if(currentChunk == null){
|
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 * spacingFactor) % ChunkData.CHUNK_SIZE,
|
|
||||||
(y * spacingFactor) % ChunkData.CHUNK_SIZE,
|
|
||||||
(z * spacingFactor) % ChunkData.CHUNK_SIZE
|
|
||||||
);
|
|
||||||
types[x][y][z] = currentChunk.getType(
|
|
||||||
(x * spacingFactor) % ChunkData.CHUNK_SIZE,
|
|
||||||
(y * spacingFactor) % ChunkData.CHUNK_SIZE,
|
|
||||||
(z * spacingFactor) % ChunkData.CHUNK_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
//checks to see if there is only one type of voxel in this chunk
|
//checks to see if there is only one type of voxel in this chunk
|
||||||
if(!this.multiVoxelType){
|
if(!this.multiVoxelType){
|
||||||
@ -233,26 +254,30 @@ public class DrawCell {
|
|||||||
*/
|
*/
|
||||||
private boolean fillInFaceData(TransvoxelChunkData chunkData, DrawCellFace higherLODFace, int lod){
|
private boolean fillInFaceData(TransvoxelChunkData chunkData, DrawCellFace higherLODFace, int lod){
|
||||||
int mainSpacing = (int)Math.pow(2,lod);
|
int mainSpacing = (int)Math.pow(2,lod);
|
||||||
int higherResSpacing = (int)Math.pow(2,lod - 1);
|
int higherLOD = lod - 1;
|
||||||
|
int higherResSpacing = (int)Math.pow(2,higherLOD);
|
||||||
float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
|
float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
|
||||||
int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
|
int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
|
||||||
//allocate face array
|
//allocate face array
|
||||||
for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){
|
for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){
|
||||||
for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){
|
for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){
|
||||||
int worldCoordOffset1 = (x * higherResSpacing) / ChunkData.CHUNK_SIZE;
|
int worldCoordOffset1 = x / ChunkData.CHUNK_SIZE * higherResSpacing;
|
||||||
int worldCoordOffset2 = (y * higherResSpacing) / ChunkData.CHUNK_SIZE;
|
int worldCoordOffset2 = y / ChunkData.CHUNK_SIZE * higherResSpacing;
|
||||||
//solve coordinates relative to the face
|
//solve coordinates relative to the face
|
||||||
int localCoord1 = (x * higherResSpacing) % ChunkData.CHUNK_SIZE;
|
int localCoord1 = x % ChunkData.CHUNK_SIZE;
|
||||||
int localCoord2 = (y * higherResSpacing) % ChunkData.CHUNK_SIZE;
|
int localCoord2 = y % ChunkData.CHUNK_SIZE;
|
||||||
|
|
||||||
//implicitly performing transforms to adapt from face-space to world & local space
|
//implicitly performing transforms to adapt from face-space to world & local space
|
||||||
switch(higherLODFace){
|
switch(higherLODFace){
|
||||||
case X_POSITIVE: {
|
case X_POSITIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE,
|
new Vector3i(
|
||||||
worldPos.y + worldCoordOffset1,
|
worldPos.x + mainSpacing,
|
||||||
worldPos.z + worldCoordOffset2
|
worldPos.y + worldCoordOffset1,
|
||||||
));
|
worldPos.z + worldCoordOffset2
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -268,11 +293,14 @@ public class DrawCell {
|
|||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
case X_NEGATIVE: {
|
case X_NEGATIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x,
|
new Vector3i(
|
||||||
worldPos.y + worldCoordOffset1,
|
worldPos.x,
|
||||||
worldPos.z + worldCoordOffset2
|
worldPos.y + worldCoordOffset1,
|
||||||
));
|
worldPos.z + worldCoordOffset2
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -288,11 +316,14 @@ public class DrawCell {
|
|||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
case Y_POSITIVE: {
|
case Y_POSITIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + worldCoordOffset1,
|
new Vector3i(
|
||||||
worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE,
|
worldPos.x + worldCoordOffset1,
|
||||||
worldPos.z + worldCoordOffset2
|
worldPos.y + mainSpacing,
|
||||||
));
|
worldPos.z + worldCoordOffset2
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -308,11 +339,14 @@ public class DrawCell {
|
|||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
case Y_NEGATIVE: {
|
case Y_NEGATIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + worldCoordOffset1,
|
new Vector3i(
|
||||||
worldPos.y,
|
worldPos.x + worldCoordOffset1,
|
||||||
worldPos.z + worldCoordOffset2
|
worldPos.y,
|
||||||
));
|
worldPos.z + worldCoordOffset2
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -328,11 +362,14 @@ public class DrawCell {
|
|||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
case Z_POSITIVE: {
|
case Z_POSITIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + worldCoordOffset1,
|
new Vector3i(
|
||||||
worldPos.y + worldCoordOffset2,
|
worldPos.x + worldCoordOffset1,
|
||||||
worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE
|
worldPos.y + worldCoordOffset2,
|
||||||
));
|
worldPos.z + mainSpacing
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -348,11 +385,14 @@ public class DrawCell {
|
|||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
case Z_NEGATIVE: {
|
case Z_NEGATIVE: {
|
||||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
|
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||||
worldPos.x + worldCoordOffset1,
|
new Vector3i(
|
||||||
worldPos.y + worldCoordOffset2,
|
worldPos.x + worldCoordOffset1,
|
||||||
worldPos.z
|
worldPos.y + worldCoordOffset2,
|
||||||
));
|
worldPos.z
|
||||||
|
),
|
||||||
|
higherLOD
|
||||||
|
);
|
||||||
if(currentChunk == null){
|
if(currentChunk == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -443,6 +483,9 @@ public class DrawCell {
|
|||||||
*/
|
*/
|
||||||
public void setHasRequested(boolean hasRequested) {
|
public void setHasRequested(boolean hasRequested) {
|
||||||
this.hasRequested = hasRequested;
|
this.hasRequested = hasRequested;
|
||||||
|
if(!this.hasRequested){
|
||||||
|
this.failedGenerationAttempts = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,6 +512,22 @@ public class DrawCell {
|
|||||||
return this.multiVoxelType;
|
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){
|
||||||
|
this.failedGenerationAttempts = this.failedGenerationAttempts + attempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -176,7 +176,7 @@ public class DrawCellManager {
|
|||||||
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||||
posToCheck.z >= 0 &&
|
posToCheck.z >= 0 &&
|
||||||
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
|
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)){
|
if(!requested.contains(requestKey)){
|
||||||
//client should request chunk data from server for each chunk necessary to create the model
|
//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){
|
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
||||||
if(Globals.clientTerrainManager != null){
|
if(Globals.clientTerrainManager != null){
|
||||||
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
|
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ,ChunkData.NO_STRIDE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -442,7 +442,7 @@ public class DrawCellManager {
|
|||||||
* @return The chunk data at the specified points
|
* @return The chunk data at the specified points
|
||||||
*/
|
*/
|
||||||
ChunkData getChunkDataAtPoint(int worldX, int worldY, int worldZ){
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import java.nio.IntBuffer;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
|
|||||||
import electrosphere.renderer.model.Model;
|
import electrosphere.renderer.model.Model;
|
||||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||||
|
import io.github.studiorailgun.HashUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages terrain storage and access on the client
|
* Manages terrain storage and access on the client
|
||||||
@ -38,6 +41,16 @@ public class ClientTerrainManager {
|
|||||||
* Locks the terrain manager (eg if adding message from network)
|
* Locks the terrain manager (eg if adding message from network)
|
||||||
*/
|
*/
|
||||||
static Semaphore lock = new Semaphore(1);
|
static Semaphore lock = new Semaphore(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum concurrent terrain requests
|
||||||
|
*/
|
||||||
|
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
|
//The interpolation ratio of terrain
|
||||||
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
||||||
@ -58,6 +71,11 @@ public class ClientTerrainManager {
|
|||||||
|
|
||||||
//The queue of terrain chunk data to be buffered to gpu
|
//The queue of terrain chunk data to be buffered to gpu
|
||||||
static List<TerrainChunkGenQueueItem> terrainChunkGenerationQueue = new CopyOnWriteArrayList<TerrainChunkGenQueueItem>();
|
static List<TerrainChunkGenQueueItem> terrainChunkGenerationQueue = new CopyOnWriteArrayList<TerrainChunkGenQueueItem>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks what outgoing requests are currently active
|
||||||
|
*/
|
||||||
|
Map<Long,Integer> requestedMap = new ConcurrentHashMap<Long,Integer>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -98,7 +116,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.setVoxelType(values);
|
||||||
data.setVoxelWeight(weights);
|
data.setVoxelWeight(weights);
|
||||||
terrainCache.addChunkDataToCache(
|
terrainCache.addChunkDataToCache(
|
||||||
@ -106,6 +124,37 @@ public class ClientTerrainManager {
|
|||||||
data
|
data
|
||||||
);
|
);
|
||||||
} break;
|
} 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(), message.getchunkResolution());
|
||||||
|
data.setVoxelType(values);
|
||||||
|
data.setVoxelWeight(weights);
|
||||||
|
terrainCache.addChunkDataToCache(
|
||||||
|
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:
|
default:
|
||||||
LoggerInterface.loggerEngine.WARNING("ClientTerrainManager: unhandled network message of type" + message.getMessageSubtype());
|
LoggerInterface.loggerEngine.WARNING("ClientTerrainManager: unhandled network message of type" + message.getMessageSubtype());
|
||||||
break;
|
break;
|
||||||
@ -114,6 +163,15 @@ public class ClientTerrainManager {
|
|||||||
for(TerrainMessage message : bouncedMessages){
|
for(TerrainMessage message : bouncedMessages){
|
||||||
messageQueue.add(message);
|
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();
|
lock.release();
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
}
|
}
|
||||||
@ -140,19 +198,21 @@ public class ClientTerrainManager {
|
|||||||
* @param worldX the x position
|
* @param worldX the x position
|
||||||
* @param worldY the y position
|
* @param worldY the y position
|
||||||
* @param worldZ the z position
|
* @param worldZ the z position
|
||||||
|
* @param stride The stride of the data
|
||||||
* @return true if the data exists, false otherwise
|
* @return true if the data exists, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){
|
||||||
return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ);
|
return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the terrain cache contains chunk data at a given world position
|
* Checks if the terrain cache contains chunk data at a given world position
|
||||||
* @param worldPos The vector containing the world-space 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
|
* @return true if the data exists, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean containsChunkDataAtWorldPoint(Vector3i worldPos){
|
public boolean containsChunkDataAtWorldPoint(Vector3i worldPos, int stride){
|
||||||
return terrainCache.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z);
|
return this.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,31 +220,24 @@ public class ClientTerrainManager {
|
|||||||
* @param worldX the world x coordinate of the chunk
|
* @param worldX the world x coordinate of the chunk
|
||||||
* @param worldY the world y coordinate of the chunk
|
* @param worldY the world y coordinate of the chunk
|
||||||
* @param worldZ the world z 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){
|
public boolean requestChunk(int worldX, int worldY, int worldZ, int stride){
|
||||||
if(!this.terrainCache.hasRequested(worldX, worldY, worldZ)){
|
boolean rVal = false;
|
||||||
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
|
lock.acquireUninterruptibly();
|
||||||
|
if(this.requestedMap.size() < MAX_CONCURRENT_REQUESTS && !this.requestedMap.containsKey(this.getRequestKey(worldX, worldY, worldZ, stride))){
|
||||||
|
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestReducedChunkDataMessage(
|
||||||
worldX,
|
worldX,
|
||||||
worldY,
|
worldY,
|
||||||
worldZ
|
worldZ,
|
||||||
|
stride
|
||||||
));
|
));
|
||||||
|
this.requestedMap.put(this.getRequestKey(worldX, worldY, worldZ, stride), 0);
|
||||||
|
rVal = true;
|
||||||
}
|
}
|
||||||
}
|
lock.release();
|
||||||
|
return rVal;
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,19 +245,21 @@ public class ClientTerrainManager {
|
|||||||
* @param worldX The x component of the world coordinate
|
* @param worldX The x component of the world coordinate
|
||||||
* @param worldY The y component of the world coordinate
|
* @param worldY The y component of the world coordinate
|
||||||
* @param worldZ The z 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
|
* @return The chunk data if it exists, otherwise null
|
||||||
*/
|
*/
|
||||||
public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
|
public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ, int stride){
|
||||||
return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ);
|
return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the chunk data at a given world position
|
* Gets the chunk data at a given world position
|
||||||
* @param worldPos The world position as a joml vector
|
* @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
|
* @return The chunk data if it exists, otherwise null
|
||||||
*/
|
*/
|
||||||
public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos){
|
public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos, int stride){
|
||||||
return terrainCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z);
|
return this.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -258,11 +313,15 @@ public class ClientTerrainManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of chunks that have been requested
|
* Gets the key for a given request
|
||||||
* @return The number of chunks
|
* @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
|
||||||
*/
|
*/
|
||||||
public int getRequestedCellCount(){
|
private Long getRequestKey(int worldX, int worldY, int worldZ, int stride){
|
||||||
return this.terrainCache.getRequestedCellCount();
|
return (long)HashUtils.cantorHash(worldY, worldZ, worldZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,8 +36,8 @@ public class ClientVoxelSampler {
|
|||||||
int voxelId = 0;
|
int voxelId = 0;
|
||||||
Vector3i chunkSpacePos = Globals.clientWorldData.convertRealToWorldSpace(realPos);
|
Vector3i chunkSpacePos = Globals.clientWorldData.convertRealToWorldSpace(realPos);
|
||||||
Vector3i voxelSpacePos = Globals.clientWorldData.convertRealToVoxelSpace(realPos);
|
Vector3i voxelSpacePos = Globals.clientWorldData.convertRealToVoxelSpace(realPos);
|
||||||
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(chunkSpacePos)){
|
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE)){
|
||||||
ChunkData chunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkSpacePos);
|
ChunkData chunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE);
|
||||||
voxelId = chunkData.getType(voxelSpacePos);
|
voxelId = chunkData.getType(voxelSpacePos);
|
||||||
} else {
|
} else {
|
||||||
return INVALID_POSITION;
|
return INVALID_POSITION;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import java.lang.management.ManagementFactory;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.joml.Matrix4d;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
@ -257,7 +258,7 @@ public class Globals {
|
|||||||
|
|
||||||
//matrices for drawing models
|
//matrices for drawing models
|
||||||
public static Matrix4f viewMatrix = new Matrix4f();
|
public static Matrix4f viewMatrix = new Matrix4f();
|
||||||
public static Matrix4f projectionMatrix;
|
public static Matrix4d projectionMatrix;
|
||||||
public static Matrix4f lightDepthMatrix = new Matrix4f();
|
public static Matrix4f lightDepthMatrix = new Matrix4f();
|
||||||
|
|
||||||
//locations for shadow map specific variables
|
//locations for shadow map specific variables
|
||||||
|
|||||||
@ -245,7 +245,7 @@ public class ClientLoading {
|
|||||||
EntityCreationUtils.makeEntityDrawable(skybox, "Models/environment/skyboxSphere.fbx");
|
EntityCreationUtils.makeEntityDrawable(skybox, "Models/environment/skyboxSphere.fbx");
|
||||||
DrawableUtils.disableCulling(skybox);
|
DrawableUtils.disableCulling(skybox);
|
||||||
EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f));
|
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");
|
Globals.assetManager.queueOverrideMeshShader("Models/environment/skyboxSphere.fbx", "Sphere", "Shaders/entities/skysphere/skysphere.vs", "Shaders/entities/skysphere/skysphere.fs");
|
||||||
|
|
||||||
//cloud ring pseudo skybox
|
//cloud ring pseudo skybox
|
||||||
@ -297,10 +297,14 @@ public class ClientLoading {
|
|||||||
Globals.clientSimulation.setLoadingTerrain(true);
|
Globals.clientSimulation.setLoadingTerrain(true);
|
||||||
//wait for all the terrain data to arrive
|
//wait for all the terrain data to arrive
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(blockForInit && Globals.clientDrawCellManager.updatedLastFrame() && Globals.threadManager.shouldKeepRunning()){
|
while(
|
||||||
|
blockForInit &&
|
||||||
|
!Globals.clientDrawCellManager.isInitialized() &&
|
||||||
|
Globals.threadManager.shouldKeepRunning()
|
||||||
|
){
|
||||||
i++;
|
i++;
|
||||||
if(i % DRAW_CELL_UPDATE_RATE == 0){
|
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 {
|
try {
|
||||||
TimeUnit.MILLISECONDS.sleep(10);
|
TimeUnit.MILLISECONDS.sleep(10);
|
||||||
|
|||||||
@ -20,6 +20,11 @@ public class TerrainChunkData {
|
|||||||
//texture ratio vector
|
//texture ratio vector
|
||||||
List<Float> textureRatioVectors; //HOW MUCH of each texture in the atlas to sample
|
List<Float> 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
|
* Creates an object to hold data required to generate a chunk
|
||||||
* @param vertices
|
* @param vertices
|
||||||
@ -27,14 +32,16 @@ public class TerrainChunkData {
|
|||||||
* @param faceElements
|
* @param faceElements
|
||||||
* @param uvs
|
* @param uvs
|
||||||
* @param textureSamplers
|
* @param textureSamplers
|
||||||
|
* @param lod The LOD of the model
|
||||||
*/
|
*/
|
||||||
public TerrainChunkData(List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs, List<Float> textureSamplers, List<Float> textureRatioVectors){
|
public TerrainChunkData(List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs, List<Float> textureSamplers, List<Float> textureRatioVectors, int lod){
|
||||||
this.vertices = vertices;
|
this.vertices = vertices;
|
||||||
this.normals = normals;
|
this.normals = normals;
|
||||||
this.faceElements = faceElements;
|
this.faceElements = faceElements;
|
||||||
this.uvs = uvs;
|
this.uvs = uvs;
|
||||||
this.textureSamplers = textureSamplers;
|
this.textureSamplers = textureSamplers;
|
||||||
this.textureRatioVectors = textureRatioVectors;
|
this.textureRatioVectors = textureRatioVectors;
|
||||||
|
this.lod = lod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,4 +92,12 @@ public class TerrainChunkData {
|
|||||||
return textureRatioVectors;
|
return textureRatioVectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LOD of the model
|
||||||
|
* @return The LOD
|
||||||
|
*/
|
||||||
|
public int getLOD(){
|
||||||
|
return lod;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
* Prints a message at the specified logging level
|
||||||
* @param level The logging level
|
* @param level The logging level
|
||||||
|
|||||||
@ -47,6 +47,10 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
|
|||||||
LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
|
LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
|
||||||
Globals.clientTerrainManager.attachTerrainMessage(message);
|
Globals.clientTerrainManager.attachTerrainMessage(message);
|
||||||
} break;
|
} 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: {
|
case UPDATEVOXEL: {
|
||||||
//
|
//
|
||||||
//find what all drawcells might be updated by this voxel update
|
//find what all drawcells might be updated by this voxel update
|
||||||
@ -79,8 +83,8 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
//update the terrain cache
|
//update the terrain cache
|
||||||
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(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 data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE);
|
||||||
if(data != null){
|
if(data != null){
|
||||||
data.updatePosition(
|
data.updatePosition(
|
||||||
message.getvoxelX(),
|
message.getvoxelX(),
|
||||||
@ -94,8 +98,8 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
|
|||||||
//
|
//
|
||||||
//mark all relevant drawcells as updateable
|
//mark all relevant drawcells as updateable
|
||||||
for(Vector3i worldPosToUpdate : positionsToUpdate){
|
for(Vector3i worldPosToUpdate : positionsToUpdate){
|
||||||
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(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 data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z, ChunkData.NO_STRIDE);
|
||||||
if(data != null){
|
if(data != null){
|
||||||
Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
|
Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.client.terrain.cache.ChunkData;
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
import electrosphere.logger.LoggerInterface;
|
import electrosphere.logger.LoggerInterface;
|
||||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||||
@ -32,6 +33,12 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
|||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case REQUESTREDUCEDCHUNKDATA: {
|
||||||
|
sendWorldSubChunkAsyncStrided(connectionHandler,
|
||||||
|
message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution()
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@ -185,7 +192,65 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//request chunk
|
//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<ServerTerrainChunk> 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.constructSendReducedChunkDataMessage(worldX, worldY, worldZ, stride, buffer.array()));
|
||||||
|
};
|
||||||
|
|
||||||
|
//request chunk
|
||||||
|
realm.getServerWorldData().getServerTerrainManager().getChunkAsync(worldX, worldY, worldZ, stride, onLoad);
|
||||||
|
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package electrosphere.renderer;
|
package electrosphere.renderer;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
import org.joml.Matrix4d;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import electrosphere.renderer.actor.instance.InstanceData;
|
import electrosphere.renderer.actor.instance.InstanceData;
|
||||||
@ -166,7 +167,7 @@ public class RenderPipelineState {
|
|||||||
* @param projectionMatrix the projection matrix
|
* @param projectionMatrix the projection matrix
|
||||||
* @param viewMatrix the view matrix
|
* @param viewMatrix the view matrix
|
||||||
*/
|
*/
|
||||||
public void updateFrustumIntersection(Matrix4f projectionMatrix, Matrix4f viewMatrix){
|
public void updateFrustumIntersection(Matrix4d projectionMatrix, Matrix4f viewMatrix){
|
||||||
Matrix4f projectionViewMatrix = new Matrix4f();
|
Matrix4f projectionViewMatrix = new Matrix4f();
|
||||||
projectionViewMatrix.set(projectionMatrix);
|
projectionViewMatrix.set(projectionMatrix);
|
||||||
projectionViewMatrix.mul(viewMatrix);
|
projectionViewMatrix.mul(viewMatrix);
|
||||||
|
|||||||
@ -453,7 +453,7 @@ public class RenderingEngine {
|
|||||||
//
|
//
|
||||||
// Projection and View matrix creation
|
// Projection and View matrix creation
|
||||||
//
|
//
|
||||||
Globals.projectionMatrix = new Matrix4f();
|
Globals.projectionMatrix = new Matrix4d();
|
||||||
Globals.viewMatrix = new Matrix4f();
|
Globals.viewMatrix = new Matrix4f();
|
||||||
verticalFOV = (float)(Globals.verticalFOV * Math.PI /180.0f);
|
verticalFOV = (float)(Globals.verticalFOV * Math.PI /180.0f);
|
||||||
//set local aspect ratio and global aspect ratio at the same time
|
//set local aspect ratio and global aspect ratio at the same time
|
||||||
|
|||||||
@ -763,7 +763,7 @@ public class TerrainChunkModelGeneration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs
|
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs
|
||||||
TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData);
|
TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData, 0);
|
||||||
return rVal;
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,14 +892,15 @@ public class TerrainChunkModelGeneration {
|
|||||||
|
|
||||||
|
|
||||||
//bounding sphere logic
|
//bounding sphere logic
|
||||||
|
int distance = ServerTerrainChunk.CHUNK_DIMENSION / 2 * (int)Math.pow(2,data.getLOD());
|
||||||
mesh.updateBoundingSphere(
|
mesh.updateBoundingSphere(
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION,
|
distance,
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION,
|
distance,
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION,
|
distance,
|
||||||
(float)Math.sqrt(
|
(float)Math.sqrt(
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION +
|
distance * distance +
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION +
|
distance * distance +
|
||||||
ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION
|
distance * distance
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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+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]
|
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
|
//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]
|
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 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 {
|
} else {
|
||||||
@ -1694,7 +1694,7 @@ public class TransvoxelModelGeneration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs
|
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> 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;
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ public class DefaultChunkGenerator implements ChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
//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
|
//Hence, width should actually be chunk dimension + 1
|
||||||
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public class OverworldChunkGenerator implements ChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) {
|
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) {
|
||||||
ServerTerrainChunk returnedChunk;
|
ServerTerrainChunk returnedChunk;
|
||||||
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
|
//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
|
//Hence, width should actually be chunk dimension + 1
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package electrosphere.server.terrain.generation;
|
package electrosphere.server.terrain.generation;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
/**
|
/**
|
||||||
* The size of the realm for testing generation
|
* The size of the realm for testing generation
|
||||||
*/
|
*/
|
||||||
public static final int GENERATOR_REALM_SIZE = 32;
|
public static final int GENERATOR_REALM_SIZE = 512;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default biome index
|
* The default biome index
|
||||||
@ -65,36 +64,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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");
|
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk");
|
||||||
ServerTerrainChunk rVal = null;
|
ServerTerrainChunk rVal = null;
|
||||||
float[][][] weights;
|
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];;
|
||||||
int[][][] values;
|
int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||||
|
|
||||||
if(worldX == 0 || worldZ == 0){
|
try {
|
||||||
//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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
//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
|
//biome of the current chunk
|
||||||
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ);
|
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ);
|
||||||
@ -105,11 +82,22 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
|
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
|
//presolve heightfield
|
||||||
float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
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++){
|
||||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
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,15 +105,22 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
||||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
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;
|
weights[x][y][z] = voxel.weight;
|
||||||
values[x][y][z] = voxel.type;
|
values[x][y][z] = voxel.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
}
|
}
|
||||||
|
} catch(Exception ex){
|
||||||
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
|
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
return rVal;
|
return rVal;
|
||||||
@ -144,7 +139,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
* @param chunkX The chunk x pos
|
* @param chunkX The chunk x pos
|
||||||
* @param chunkY The chunk y pos
|
* @param chunkY The chunk y pos
|
||||||
* @param chunkZ The chunk z 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 terrainModel The terrain model
|
||||||
* @param surfaceBiome The surface biome of the chunk
|
* @param surfaceBiome The surface biome of the chunk
|
||||||
* @return The value of the chunk
|
* @return The value of the chunk
|
||||||
@ -152,7 +147,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
private GeneratedVoxel getVoxel(
|
private GeneratedVoxel getVoxel(
|
||||||
int worldX, int worldY, int worldZ,
|
int worldX, int worldY, int worldZ,
|
||||||
int chunkX, int chunkY, int chunkZ,
|
int chunkX, int chunkY, int chunkZ,
|
||||||
float[][] heightfield,
|
float surfaceHeight,
|
||||||
TerrainModel terrainModel,
|
TerrainModel terrainModel,
|
||||||
BiomeData surfaceBiome
|
BiomeData surfaceBiome
|
||||||
){
|
){
|
||||||
@ -163,16 +158,15 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
|
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 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);
|
double flooredSurfaceHeight = Math.floor(surfaceHeight);
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
if(realY < surfaceHeight - 1){
|
if(realY < surfaceHeight - 1){
|
||||||
return getSubsurfaceVoxel(
|
return getSubsurfaceVoxel(
|
||||||
worldX,worldY, worldZ,
|
worldX, worldY, worldZ,
|
||||||
chunkX, chunkY, chunkZ,
|
chunkX, chunkY, chunkZ,
|
||||||
realX, realY, realZ,
|
realX, realY, realZ,
|
||||||
surfaceHeight, flooredSurfaceHeight,
|
surfaceHeight, flooredSurfaceHeight,
|
||||||
@ -181,7 +175,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
);
|
);
|
||||||
} else if(realY > flooredSurfaceHeight) {
|
} else if(realY > flooredSurfaceHeight) {
|
||||||
return getOverSurfaceVoxel(
|
return getOverSurfaceVoxel(
|
||||||
worldX,worldY, worldZ,
|
worldX, worldY, worldZ,
|
||||||
chunkX, chunkY, chunkZ,
|
chunkX, chunkY, chunkZ,
|
||||||
realX, realY, realZ,
|
realX, realY, realZ,
|
||||||
surfaceHeight, flooredSurfaceHeight,
|
surfaceHeight, flooredSurfaceHeight,
|
||||||
@ -190,7 +184,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return getSurfaceVoxel(
|
return getSurfaceVoxel(
|
||||||
worldX,worldY, worldZ,
|
worldX, worldY, worldZ,
|
||||||
chunkX, chunkY, chunkZ,
|
chunkX, chunkY, chunkZ,
|
||||||
realX, realY, realZ,
|
realX, realY, realZ,
|
||||||
surfaceHeight, flooredSurfaceHeight,
|
surfaceHeight, flooredSurfaceHeight,
|
||||||
|
|||||||
@ -13,9 +13,10 @@ public interface ChunkGenerator {
|
|||||||
* @param worldX The x component
|
* @param worldX The x component
|
||||||
* @param worldY The y component
|
* @param worldY The y component
|
||||||
* @param worldZ The z component
|
* @param worldZ The z component
|
||||||
|
* @param stride The stride of the data
|
||||||
* @return The chunk
|
* @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
|
* Sets the terrain model for the generation algorithm
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
|
import electrosphere.logger.LoggerInterface;
|
||||||
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
|
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
|
||||||
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
|
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
|
||||||
|
|
||||||
@ -51,6 +52,11 @@ public class ChunkGenerationThread implements Runnable {
|
|||||||
* The world z coordinate
|
* The world z coordinate
|
||||||
*/
|
*/
|
||||||
int worldZ;
|
int worldZ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stride of the data
|
||||||
|
*/
|
||||||
|
int stride;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The work to do once the chunk is available
|
* The work to do once the chunk is available
|
||||||
@ -65,6 +71,7 @@ public class ChunkGenerationThread implements Runnable {
|
|||||||
* @param worldX The world x coordinate
|
* @param worldX The world x coordinate
|
||||||
* @param worldY The world y coordinate
|
* @param worldY The world y coordinate
|
||||||
* @param worldZ The world z 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
|
* @param onLoad The work to do once the chunk is available
|
||||||
*/
|
*/
|
||||||
public ChunkGenerationThread(
|
public ChunkGenerationThread(
|
||||||
@ -72,6 +79,7 @@ public class ChunkGenerationThread implements Runnable {
|
|||||||
ServerChunkCache chunkCache,
|
ServerChunkCache chunkCache,
|
||||||
ChunkGenerator chunkGenerator,
|
ChunkGenerator chunkGenerator,
|
||||||
int worldX, int worldY, int worldZ,
|
int worldX, int worldY, int worldZ,
|
||||||
|
int stride,
|
||||||
Consumer<ServerTerrainChunk> onLoad
|
Consumer<ServerTerrainChunk> onLoad
|
||||||
){
|
){
|
||||||
this.chunkDiskMap = chunkDiskMap;
|
this.chunkDiskMap = chunkDiskMap;
|
||||||
@ -80,6 +88,7 @@ public class ChunkGenerationThread implements Runnable {
|
|||||||
this.worldX = worldX;
|
this.worldX = worldX;
|
||||||
this.worldY = worldY;
|
this.worldY = worldY;
|
||||||
this.worldZ = worldZ;
|
this.worldZ = worldZ;
|
||||||
|
this.stride = stride;
|
||||||
this.onLoad = onLoad;
|
this.onLoad = onLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,37 +96,41 @@ public class ChunkGenerationThread implements Runnable {
|
|||||||
public void run() {
|
public void run() {
|
||||||
ServerTerrainChunk chunk = null;
|
ServerTerrainChunk chunk = null;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){
|
try {
|
||||||
if(chunkCache.containsChunk(worldX,worldY,worldZ)){
|
while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){
|
||||||
chunk = chunkCache.get(worldX,worldY,worldZ);
|
if(chunkCache.containsChunk(worldX, worldY, worldZ, stride)){
|
||||||
} else {
|
chunk = chunkCache.get(worldX, worldY, worldZ, stride);
|
||||||
//pull from disk if it exists
|
} else {
|
||||||
if(chunkDiskMap != null){
|
//pull from disk if it exists
|
||||||
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
|
if(chunkDiskMap != null){
|
||||||
chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
|
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){
|
if(chunk == null){
|
||||||
chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
|
try {
|
||||||
}
|
TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
|
||||||
if(chunk != null){
|
} catch (InterruptedException e) {
|
||||||
chunkCache.add(worldX, worldY, worldZ, chunk);
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
if(chunk == null){
|
if(i >= MAX_TIME_TO_WAIT){
|
||||||
try {
|
throw new Error("Failed to resolve chunk!");
|
||||||
TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,11 @@ import java.util.HashMap;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
import io.github.studiorailgun.HashUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caches chunk data on the server
|
* Caches chunk data on the server
|
||||||
*/
|
*/
|
||||||
@ -16,7 +19,7 @@ public class ServerChunkCache {
|
|||||||
/**
|
/**
|
||||||
* Number of chunks to cache
|
* Number of chunks to cache
|
||||||
*/
|
*/
|
||||||
static final int CACHE_SIZE = 5000;
|
static final int CACHE_SIZE = 2500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size of the cache
|
* The size of the cache
|
||||||
@ -24,19 +27,39 @@ public class ServerChunkCache {
|
|||||||
int cacheSize = CACHE_SIZE;
|
int cacheSize = CACHE_SIZE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cached data
|
* The map of full res chunk key -> chunk data
|
||||||
*/
|
*/
|
||||||
Map<String, ServerTerrainChunk> chunkCache = new HashMap<String,ServerTerrainChunk>();
|
Map<Long,ServerTerrainChunk> cacheMapFullRes = new ConcurrentHashMap<Long,ServerTerrainChunk>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of half res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ServerTerrainChunk> cacheMapHalfRes = new ConcurrentHashMap<Long,ServerTerrainChunk>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of quarter res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ServerTerrainChunk> cacheMapQuarterRes = new ConcurrentHashMap<Long,ServerTerrainChunk>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of eighth res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ServerTerrainChunk> cacheMapEighthRes = new ConcurrentHashMap<Long,ServerTerrainChunk>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of sixteenth res chunk key -> chunk data
|
||||||
|
*/
|
||||||
|
Map<Long,ServerTerrainChunk> cacheMapSixteenthRes = new ConcurrentHashMap<Long,ServerTerrainChunk>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks how recently a chunk has been queries for (used for evicting old chunks from cache)
|
* Tracks how recently a chunk has been queries for (used for evicting old chunks from cache)
|
||||||
*/
|
*/
|
||||||
List<String> queryRecencyQueue = new LinkedList<String>();
|
List<Long> queryRecencyQueue = new LinkedList<Long>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks what chunks are already queued to be asynchronously loaded. Used so we don't have two threads generating/fetching the same chunk
|
* Tracks what chunks are already queued to be asynchronously loaded. Used so we don't have two threads generating/fetching the same chunk
|
||||||
*/
|
*/
|
||||||
Map<String, Boolean> queuedChunkMap = new HashMap<String,Boolean>();
|
Map<Long, Boolean> queuedChunkMap = new HashMap<Long,Boolean>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The lock for thread safety
|
* The lock for thread safety
|
||||||
@ -49,7 +72,7 @@ public class ServerChunkCache {
|
|||||||
*/
|
*/
|
||||||
public Collection<ServerTerrainChunk> getContents(){
|
public Collection<ServerTerrainChunk> getContents(){
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
Collection<ServerTerrainChunk> rVal = Collections.unmodifiableCollection(chunkCache.values());
|
Collection<ServerTerrainChunk> rVal = Collections.unmodifiableCollection(cacheMapFullRes.values());
|
||||||
lock.release();
|
lock.release();
|
||||||
return rVal;
|
return rVal;
|
||||||
}
|
}
|
||||||
@ -59,7 +82,8 @@ public class ServerChunkCache {
|
|||||||
*/
|
*/
|
||||||
public void clear(){
|
public void clear(){
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
chunkCache.clear();
|
cacheMapFullRes.clear();
|
||||||
|
cacheMapHalfRes.clear();
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +92,17 @@ public class ServerChunkCache {
|
|||||||
* @param worldX The world x coordinate
|
* @param worldX The world x coordinate
|
||||||
* @param worldY The world y coordinate
|
* @param worldY The world y coordinate
|
||||||
* @param worldZ The world z coordinate
|
* @param worldZ The world z coordinate
|
||||||
|
* @param stride The stride of the data
|
||||||
* @return The chunk
|
* @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;
|
ServerTerrainChunk rVal = null;
|
||||||
String key = this.getKey(worldX, worldY, worldZ);
|
Long key = this.getKey(worldX, worldY, worldZ);
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
queryRecencyQueue.remove(key);
|
queryRecencyQueue.remove(key);
|
||||||
queryRecencyQueue.add(0, key);
|
queryRecencyQueue.add(0, key);
|
||||||
rVal = this.chunkCache.get(key);
|
Map<Long,ServerTerrainChunk> cache = this.getCache(stride);
|
||||||
|
rVal = cache.get(key);
|
||||||
lock.release();
|
lock.release();
|
||||||
return rVal;
|
return rVal;
|
||||||
}
|
}
|
||||||
@ -86,13 +112,15 @@ public class ServerChunkCache {
|
|||||||
* @param worldX The world x coordinate of the chunk
|
* @param worldX The world x coordinate of the chunk
|
||||||
* @param worldY The world y coordinate of the chunk
|
* @param worldY The world y coordinate of the chunk
|
||||||
* @param worldZ The world z 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
|
* @param chunk The chunk itself
|
||||||
*/
|
*/
|
||||||
public void add(int worldX, int worldY, int worldZ, ServerTerrainChunk chunk){
|
public void add(int worldX, int worldY, int worldZ, int stride, ServerTerrainChunk chunk){
|
||||||
String key = this.getKey(worldX, worldY, worldZ);
|
Long key = this.getKey(worldX, worldY, worldZ);
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
queryRecencyQueue.add(0, key);
|
queryRecencyQueue.add(0, key);
|
||||||
this.chunkCache.put(key, chunk);
|
Map<Long,ServerTerrainChunk> cache = this.getCache(stride);
|
||||||
|
cache.put(key, chunk);
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,12 +129,14 @@ public class ServerChunkCache {
|
|||||||
* @param worldX The world x coordinate
|
* @param worldX The world x coordinate
|
||||||
* @param worldY The world y coordinate
|
* @param worldY The world y coordinate
|
||||||
* @param worldZ The world z coordinate
|
* @param worldZ The world z coordinate
|
||||||
|
* @param stride The stride of the data
|
||||||
* @return true if the cache contains this chunk, false otherwise
|
* @return true if the cache contains this chunk, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean containsChunk(int worldX, int worldY, int worldZ){
|
public boolean containsChunk(int worldX, int worldY, int worldZ, int stride){
|
||||||
String key = this.getKey(worldX,worldY,worldZ);
|
Long key = this.getKey(worldX,worldY,worldZ);
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
boolean rVal = this.chunkCache.containsKey(key);
|
Map<Long,ServerTerrainChunk> cache = this.getCache(stride);
|
||||||
|
boolean rVal = cache.containsKey(key);
|
||||||
lock.release();
|
lock.release();
|
||||||
return rVal;
|
return rVal;
|
||||||
}
|
}
|
||||||
@ -118,8 +148,8 @@ public class ServerChunkCache {
|
|||||||
* @param worldZ The z component
|
* @param worldZ The z component
|
||||||
* @return The key
|
* @return The key
|
||||||
*/
|
*/
|
||||||
public String getKey(int worldX, int worldY, int worldZ){
|
public long getKey(int worldX, int worldY, int worldZ){
|
||||||
return worldX + "_" + worldY + "_" + worldZ;
|
return HashUtils.cantorHash(worldX, worldY, worldZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,7 +160,7 @@ public class ServerChunkCache {
|
|||||||
* @return true if the chunk is already queued, false otherwise
|
* @return true if the chunk is already queued, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean chunkIsQueued(int worldX, int worldY, int worldZ){
|
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();
|
lock.acquireUninterruptibly();
|
||||||
boolean rVal = this.queuedChunkMap.containsKey(key);
|
boolean rVal = this.queuedChunkMap.containsKey(key);
|
||||||
lock.release();
|
lock.release();
|
||||||
@ -144,7 +174,7 @@ public class ServerChunkCache {
|
|||||||
* @param worldZ The world z position of the chunk
|
* @param worldZ The world z position of the chunk
|
||||||
*/
|
*/
|
||||||
public void queueChunk(int worldX, int worldY, int worldZ){
|
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();
|
lock.acquireUninterruptibly();
|
||||||
this.queuedChunkMap.put(key,true);
|
this.queuedChunkMap.put(key,true);
|
||||||
lock.release();
|
lock.release();
|
||||||
@ -155,12 +185,41 @@ public class ServerChunkCache {
|
|||||||
* @param worldX The world x position of the chunk
|
* @param worldX The world x position of the chunk
|
||||||
* @param worldY The world y position of the chunk
|
* @param worldY The world y position of the chunk
|
||||||
* @param worldZ The world z 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){
|
public void unqueueChunk(int worldX, int worldY, int worldZ, int stride){
|
||||||
String key = this.getKey(worldX,worldY,worldZ);
|
Long key = this.getKey(worldX,worldY,worldZ);
|
||||||
lock.acquireUninterruptibly();
|
lock.acquireUninterruptibly();
|
||||||
this.queuedChunkMap.remove(key);
|
this.queuedChunkMap.remove(key);
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cache
|
||||||
|
* @param stride The stride of the data
|
||||||
|
* @return The cache to use
|
||||||
|
*/
|
||||||
|
public Map<Long,ServerTerrainChunk> getCache(int stride){
|
||||||
|
switch(stride){
|
||||||
|
case 0: {
|
||||||
|
return cacheMapFullRes;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
return cacheMapHalfRes;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
return cacheMapQuarterRes;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
return cacheMapEighthRes;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
return cacheMapSixteenthRes;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error("Invalid stride probided! " + stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package electrosphere.server.terrain.manager;
|
package electrosphere.server.terrain.manager;
|
||||||
|
|
||||||
|
import electrosphere.client.terrain.cache.ChunkData;
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
import electrosphere.game.server.world.ServerWorldData;
|
import electrosphere.game.server.world.ServerWorldData;
|
||||||
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
|
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
|
||||||
@ -231,8 +232,8 @@ public class ServerTerrainManager {
|
|||||||
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk");
|
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk");
|
||||||
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
||||||
ServerTerrainChunk returnedChunk = null;
|
ServerTerrainChunk returnedChunk = null;
|
||||||
if(chunkCache.containsChunk(worldX,worldY,worldZ)){
|
if(chunkCache.containsChunk(worldX,worldY,worldZ,ChunkData.NO_STRIDE)){
|
||||||
returnedChunk = chunkCache.get(worldX,worldY,worldZ);
|
returnedChunk = chunkCache.get(worldX,worldY,worldZ, ChunkData.NO_STRIDE);
|
||||||
} else {
|
} else {
|
||||||
//pull from disk if it exists
|
//pull from disk if it exists
|
||||||
if(chunkDiskMap != null){
|
if(chunkDiskMap != null){
|
||||||
@ -242,9 +243,9 @@ public class ServerTerrainManager {
|
|||||||
}
|
}
|
||||||
//generate if it does not exist
|
//generate if it does not exist
|
||||||
if(returnedChunk == null){
|
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);
|
this.chunkCache.add(worldX, worldY, worldZ, ChunkData.NO_STRIDE, returnedChunk);
|
||||||
}
|
}
|
||||||
Globals.profiler.endCpuSample();
|
Globals.profiler.endCpuSample();
|
||||||
return returnedChunk;
|
return returnedChunk;
|
||||||
@ -255,11 +256,12 @@ public class ServerTerrainManager {
|
|||||||
* @param worldX The world x position
|
* @param worldX The world x position
|
||||||
* @param worldY The world y position
|
* @param worldY The world y position
|
||||||
* @param worldZ The world z 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
|
* @param onLoad The logic to run once the chunk is available
|
||||||
*/
|
*/
|
||||||
public void getChunkAsync(int worldX, int worldY, int worldZ, Consumer<ServerTerrainChunk> onLoad){
|
public void getChunkAsync(int worldX, int worldY, int worldZ, int stride, Consumer<ServerTerrainChunk> onLoad){
|
||||||
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunkAsync");
|
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();
|
Globals.profiler.endCpuSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,8 +289,8 @@ public class ServerTerrainManager {
|
|||||||
if(model != null){
|
if(model != null){
|
||||||
model.addModification(modification);
|
model.addModification(modification);
|
||||||
}
|
}
|
||||||
if(chunkCache.containsChunk(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);
|
ServerTerrainChunk chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z, ChunkData.NO_STRIDE);
|
||||||
chunk.addModification(modification);
|
chunk.addModification(modification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,11 +127,13 @@ public class TerrainModel {
|
|||||||
TerrainModel rVal = new TerrainModel();
|
TerrainModel rVal = new TerrainModel();
|
||||||
rVal.discreteArrayDimension = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE;
|
rVal.discreteArrayDimension = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE;
|
||||||
rVal.dynamicInterpolationRatio = 1;
|
rVal.dynamicInterpolationRatio = 1;
|
||||||
rVal.biome = new short[2][2];
|
int macroDataImageScale = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE / DEFAULT_MACRO_DATA_SCALE + 1;
|
||||||
rVal.biome[0][0] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
|
rVal.biome = new short[macroDataImageScale][macroDataImageScale];
|
||||||
rVal.biome[1][0] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
|
for(int x = 0; x < macroDataImageScale; x++){
|
||||||
rVal.biome[0][1] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
|
for(int z = 0; z < macroDataImageScale; z++){
|
||||||
rVal.biome[1][1] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
|
rVal.biome[x][z] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
return rVal;
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user