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

View File

@ -25,6 +25,11 @@ public class ClientDrawCellManager {
*/
static final int UPDATE_ATTEMPTS_PER_FRAME = 3;
/**
* The number of generation attempts before a cell is marked as having not requested its data
*/
static final int FAILED_GENERATION_ATTEMPT_THRESHOLD = 250;
/**
* The distance to draw at full resolution
*/
@ -35,6 +40,11 @@ public class ClientDrawCellManager {
*/
public static final double HALF_RES_DIST = 16 * ServerTerrainChunk.CHUNK_DIMENSION;
/**
* The distance for quarter resolution
*/
public static final double QUARTER_RES_DIST = 20 * ServerTerrainChunk.CHUNK_DIMENSION;
/**
* The octree holding all the chunks to evaluate
*/
@ -164,7 +174,8 @@ public class ClientDrawCellManager {
} else if(shouldRequest(playerPos, node)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
DrawCell cell = node.getData();
this.requestChunks(node);
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
this.requestChunks(node, highResFaces);
cell.setHasRequested(true);
Globals.profiler.endCpuSample();
updated = true;
@ -174,6 +185,11 @@ public class ClientDrawCellManager {
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
if(containsDataToGenerate(node,highResFaces)){
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
} else if(node.getData() != null){
node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1);
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
node.getData().setHasRequested(false);
}
}
Globals.profiler.endCpuSample();
updated = true;
@ -215,6 +231,11 @@ public class ClientDrawCellManager {
node.isLeaf() &&
node.canSplit() &&
(
(
node.getLevel() < this.chunkTree.getMaxLevel() - 2 &&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
||
(
node.getLevel() < this.chunkTree.getMaxLevel() - 1 &&
this.getMinDistance(pos, node) <= HALF_RES_DIST
@ -358,10 +379,15 @@ public class ClientDrawCellManager {
node.getLevel() == this.chunkTree.getMaxLevel() - 1 &&
this.getMinDistance(pos, node) > FULL_RES_DIST
)
||
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 2 &&
this.getMinDistance(pos, node) > HALF_RES_DIST
)
||
(
this.getMinDistance(pos, node) > QUARTER_RES_DIST
)
)
;
}
@ -389,6 +415,12 @@ public class ClientDrawCellManager {
&&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 2
&&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
)
;
}
@ -410,12 +442,18 @@ public class ClientDrawCellManager {
// &&
// this.getMinDistance(pos, node) <= FULL_RES_DIST
)
||
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 1
&&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 2
&&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
)
)
;
}
@ -494,7 +532,7 @@ public class ClientDrawCellManager {
* Requests all chunks for a given draw cell
* @param cell The cell
*/
private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node){
private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
DrawCell cell = node.getData();
int lod = this.chunkTree.getMaxLevel() - node.getLevel();
int spacingFactor = (int)Math.pow(2,lod);
@ -518,6 +556,51 @@ public class ClientDrawCellManager {
}
}
}
int highResLod = this.chunkTree.getMaxLevel() - (node.getLevel() + 1);
int highResSpacingFactor = (int)Math.pow(2,highResLod);
if(highResFaces != null){
for(DrawCellFace highResFace : highResFaces){
//x & y are in face-space
for(int x = 0; x < 3; x++){
for(int y = 0; y < 3; y++){
Vector3i posToCheck = null;
//implicitly performing transforms to adapt from face-space to world space
switch(highResFace){
case X_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(spacingFactor,x*highResSpacingFactor,y*highResSpacingFactor);
} break;
case X_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(0,x*highResSpacingFactor,y*highResSpacingFactor);
} break;
case Y_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,spacingFactor,y*highResSpacingFactor);
} break;
case Y_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,0,y*highResSpacingFactor);
} break;
case Z_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,spacingFactor);
} break;
case Z_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x*highResSpacingFactor,y*highResSpacingFactor,0);
} break;
}
if(
posToCheck.x >= 0 &&
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.y >= 0 &&
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.z >= 0 &&
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z, highResLod)
){
LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck);
Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z, highResLod);
}
}
}
}
}
}
/**

View File

@ -14,7 +14,6 @@ import electrosphere.entity.Entity;
import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/**
* A single drawcell - contains an entity that has a physics mesh and potentially graphics
@ -88,6 +87,11 @@ public class DrawCell {
* True if there are multiple types of voxels in this chunk
*/
boolean multiVoxelType = false;
/**
* Number of failed generation attempts
*/
int failedGenerationAttempts = 0;
/**
@ -118,6 +122,7 @@ public class DrawCell {
Globals.profiler.endCpuSample();
if(!success){
this.setHasRequested(false);
this.failedGenerationAttempts++;
return;
}
if(modelEntity != null){
@ -131,6 +136,7 @@ public class DrawCell {
Globals.profiler.endCpuSample();
if(!success){
this.setHasRequested(false);
this.failedGenerationAttempts++;
return;
}
}
@ -464,6 +470,9 @@ public class DrawCell {
*/
public void setHasRequested(boolean hasRequested) {
this.hasRequested = hasRequested;
if(!this.hasRequested){
this.failedGenerationAttempts = 0;
}
}
/**
@ -490,6 +499,22 @@ public class DrawCell {
return this.multiVoxelType;
}
/**
* Gets the number of failed generation attempts
* @return The number of failed generation attempts
*/
public int getFailedGenerationAttempts(){
return failedGenerationAttempts;
}
/**
* Sets the number of failed generation attempts
* @param attempts The number of failed generation attempts
*/
public void setFailedGenerationAttempts(int attempts){
failedGenerationAttempts = failedGenerationAttempts + 1;
}
}

View File

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