Add quarter res chunks + good requeue mechanism

This commit is contained in:
austin 2024-11-07 09:17:46 -05:00
parent c045c0a0d4
commit d077f43188
4 changed files with 129 additions and 5 deletions

View File

@ -31,6 +31,11 @@ public class ClientTerrainCache {
*/ */
Map<Long,ChunkData> cacheMapHalfRes = new ConcurrentHashMap<Long,ChunkData>(); Map<Long,ChunkData> cacheMapHalfRes = new ConcurrentHashMap<Long,ChunkData>();
/**
* The map of half res chunk key -> chunk data
*/
Map<Long,ChunkData> cacheMapQuarterRes = new ConcurrentHashMap<Long,ChunkData>();
/** /**
* The list of keys in the cache * The list of keys in the cache
*/ */
@ -146,6 +151,9 @@ public class ClientTerrainCache {
case 1: { case 1: {
return cacheMapHalfRes; return cacheMapHalfRes;
} }
case 2: {
return cacheMapQuarterRes;
}
default: { default: {
throw new Error("Invalid stride probided! " + stride); throw new Error("Invalid stride probided! " + stride);
} }

View File

@ -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,11 @@ 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 octree holding all the chunks to evaluate * The octree holding all the chunks to evaluate
*/ */
@ -164,7 +174,8 @@ public class ClientDrawCellManager {
} else if(shouldRequest(playerPos, node)){ } else if(shouldRequest(playerPos, node)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.request"); Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
DrawCell cell = node.getData(); DrawCell cell = node.getData();
this.requestChunks(node); List<DrawCellFace> highResFaces = this.solveHighResFace(node);
this.requestChunks(node, highResFaces);
cell.setHasRequested(true); cell.setHasRequested(true);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
updated = true; updated = true;
@ -174,6 +185,11 @@ public class ClientDrawCellManager {
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);
} 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;
@ -215,6 +231,11 @@ public class ClientDrawCellManager {
node.isLeaf() && node.isLeaf() &&
node.canSplit() && node.canSplit() &&
( (
(
node.getLevel() < this.chunkTree.getMaxLevel() - 2 &&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
||
( (
node.getLevel() < this.chunkTree.getMaxLevel() - 1 && node.getLevel() < this.chunkTree.getMaxLevel() - 1 &&
this.getMinDistance(pos, node) <= HALF_RES_DIST this.getMinDistance(pos, node) <= HALF_RES_DIST
@ -358,10 +379,15 @@ public class ClientDrawCellManager {
node.getLevel() == this.chunkTree.getMaxLevel() - 1 && node.getLevel() == this.chunkTree.getMaxLevel() - 1 &&
this.getMinDistance(pos, node) > FULL_RES_DIST this.getMinDistance(pos, node) > FULL_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - 2 &&
this.getMinDistance(pos, node) > HALF_RES_DIST this.getMinDistance(pos, node) > HALF_RES_DIST
) )
||
(
this.getMinDistance(pos, node) > QUARTER_RES_DIST
)
) )
; ;
} }
@ -389,6 +415,12 @@ public class ClientDrawCellManager {
&& &&
this.getMinDistance(pos, node) <= HALF_RES_DIST this.getMinDistance(pos, node) <= HALF_RES_DIST
) )
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 2
&&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
) )
; ;
} }
@ -410,12 +442,18 @@ public class ClientDrawCellManager {
// && // &&
// this.getMinDistance(pos, node) <= FULL_RES_DIST // this.getMinDistance(pos, node) <= FULL_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - 1 node.getLevel() == this.chunkTree.getMaxLevel() - 1
&& &&
this.getMinDistance(pos, node) <= HALF_RES_DIST this.getMinDistance(pos, node) <= HALF_RES_DIST
) )
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 2
&&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
) )
; ;
} }
@ -494,7 +532,7 @@ public class ClientDrawCellManager {
* Requests all chunks for a given draw cell * Requests all chunks for a given draw cell
* @param cell The cell * @param cell The cell
*/ */
private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node){ private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
DrawCell cell = node.getData(); DrawCell cell = node.getData();
int lod = this.chunkTree.getMaxLevel() - node.getLevel(); int lod = this.chunkTree.getMaxLevel() - node.getLevel();
int spacingFactor = (int)Math.pow(2,lod); int spacingFactor = (int)Math.pow(2,lod);
@ -518,6 +556,51 @@ public class ClientDrawCellManager {
} }
} }
} }
int highResLod = this.chunkTree.getMaxLevel() - (node.getLevel() + 1);
int highResSpacingFactor = (int)Math.pow(2,highResLod);
if(highResFaces != null){
for(DrawCellFace highResFace : highResFaces){
//x & y are in face-space
for(int x = 0; x < 3; x++){
for(int y = 0; y < 3; y++){
Vector3i posToCheck = null;
//implicitly performing transforms to adapt from face-space to world space
switch(highResFace){
case X_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(spacingFactor,x*highResSpacingFactor,y*highResSpacingFactor);
} break;
case X_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(0,x*highResSpacingFactor,y*highResSpacingFactor);
} break;
case Y_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,spacingFactor,y*highResSpacingFactor);
} break;
case Y_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,0,y*highResSpacingFactor);
} break;
case Z_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,spacingFactor);
} break;
case Z_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,0);
} break;
}
if(
posToCheck.x >= 0 &&
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.y >= 0 &&
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.z >= 0 &&
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod)
){
LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck);
Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod);
}
}
}
}
}
} }
/** /**

View File

@ -14,7 +14,6 @@ import electrosphere.entity.Entity;
import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/** /**
* A single drawcell - contains an entity that has a physics mesh and potentially graphics * A single drawcell - contains an entity that has a physics mesh and potentially graphics
@ -88,6 +87,11 @@ public class DrawCell {
* True if there are multiple types of voxels in this chunk * 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;
/** /**
@ -118,6 +122,7 @@ public class DrawCell {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
if(!success){ if(!success){
this.setHasRequested(false); this.setHasRequested(false);
this.failedGenerationAttempts++;
return; return;
} }
if(modelEntity != null){ if(modelEntity != null){
@ -131,6 +136,7 @@ public class DrawCell {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
if(!success){ if(!success){
this.setHasRequested(false); this.setHasRequested(false);
this.failedGenerationAttempts++;
return; return;
} }
} }
@ -464,6 +470,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;
}
} }
/** /**
@ -490,6 +499,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){
failedGenerationAttempts = failedGenerationAttempts + 1;
}
} }

View File

@ -36,6 +36,11 @@ public class ServerChunkCache {
*/ */
Map<Long,ServerTerrainChunk> cacheMapHalfRes = new ConcurrentHashMap<Long,ServerTerrainChunk>(); 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>();
/** /**
* 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)
*/ */
@ -192,6 +197,9 @@ public class ServerChunkCache {
case 1: { case 1: {
return cacheMapHalfRes; return cacheMapHalfRes;
} }
case 2: {
return cacheMapQuarterRes;
}
default: { default: {
throw new Error("Invalid stride probided! " + stride); throw new Error("Invalid stride probided! " + stride);
} }